MongoDB access guidelines (A House Divided)
This document complements AGENTS.md (in the AHDGame app repo) database conventions. It separates safe code-level practices from database administration decisions that need human review in production.
Connection and client#
- Use
getDb()from@/lib/mongodbin application code. It reuses a pooledMongoClientacross serverless invocations and selects the database fromMONGODB_DB/MONGO_DB_NAMEwhen present, otherwise from the database embedded inMONGODB_URI. - Scripts (outside Next.js) should use
connectDb()/closeDb()from as documented inAGENTS.md.
Collection access: two supported patterns#
- Typed collection helpers in
src/lib/db/collections/, preferred when a helper already exists or you are touching a hot path that should stay consistent (e.g.getUsersCollection,getCharactersCollection,getGameStateCollection,getPartyBudgetCollection). - Direct access,
db.collection<DocumentType>("collectionName")is acceptable and common; keep the generic correct and the name exactly as in existing code (camelCase collection names).
Do not introduce a repository framework or generic ORM layer unless there is a strong, explicit need.
Passing Db through call chains#
When code already holds const db = await getDb() (turn processing, admin batch routes, migrations), pass db into collection helpers that accept an optional Db:
const users = await getUsersCollection(db);
const characters = await getCharactersCollection(db);
That avoids redundant getDb() awaits and keeps a single logical scope for one request or one turn.
Not every helper accepts a Db. For example,
getPartyBudgetCollection() currently opens through getDb() and takes no
argument. Check the helper signature before passing a connection.
Typing and shapes#
- Document types live in
src/lib/db/types/. Usedb.collection<MyType>("myCollection")(or helpers) so queries and updates stay aligned with the schema. - Avoid untyped
db.collection("name")in new code unless the collection is truly schemaless; fixing an existing untyped read is a small, safe improvement when you are already editing the file.
Queries: hygiene and performance#
- Prefer explicit filters with fields that match expected indexes (see deferred recommendations below). Avoid relying on unindexed sort or regex prefixes without review.
- Unbounded
find({}).toArray()is appropriate only when the result set is bounded by design (e.g. every character for a turn, small config sets). For player-facing lists, uselimit,skip(with care), andprojectto reduce payload size. demographicCategoriesand similar reference data are intentionally loaded fully in some simulation paths; treat changes to that pattern as a design decision, not a drive-by optimization.
Transactions and consistency#
- wraps
withTransaction/ClientSessionand is used across money-moving routes and commands, including forex, transfers, canvassing, unions, and corporations. It attempts a real transaction when the Mongo topology supports it and falls back to the sequential implementation otherwise. Production topology is an operational fact that can change, so verify it before assuming the transaction or fallback path. - Code-level: document ordering where it matters; prefer clear phase boundaries (see turn system) over implicit “transaction-like” assumptions in random routes. Use
runWithOptionalTransactionwhere partial-write risk is high, but do not assume it is atomic in prod today. - DB-level: true atomicity across collections requires MongoDB multi-document transactions and appropriate write concern on a replica-set-capable deployment, that is an operational and design decision, not something to fake in application code.
When adding cross-collection updates, consider: (1) idempotency where possible, (2) admin heal routes for known failure modes, (3) explicit documentation in the relevant ahd-docs design page.
Indexing and schema changes (human review)#
These are not substitutes for code review of query patterns:
- Adding or changing indexes in production (Atlas or migration scripts).
- Unique constraints, TTL indexes, or partial indexes.
- Large backfills or migrations that touch many documents.
Track those in release notes, run during maintenance windows when appropriate, and validate on staging with representative data.
Index concerns implied by common patterns (for DB owners)#
The following are observations for index planning; verify with explain and production metrics:
characters: full scans forprocessTurnload all characters, expected for simulation scale; ensure RAM/query budget is acceptable as player counts grow.elections/electionVoteTallies/electionCandidates: queries often filter byelectionId,stateId,countryId, status, and time fields, compound indexes should match real filter combinations.users:_idlookups for auth are naturally indexed; avoid adding slow patterns (e.g. unindexed email regex) without an index strategy.
Testing#
- Unit / integration tests often mock
@/lib/mongodbwithcreateMockDb(). - When changing how routes or auth load data, prefer extending existing tests or adding focused tests that assert observable behavior (responses, counts), not MongoDB internals.
Related docs#
repo-operating-map.md, architecture zones and blast radius.architecture-boundaries.md, layering rules forsrc/lib/turn/and API routes.- The ahd-docs design section for simulation invariants such as elections, turn order, and NPP behavior.