Files
template-app/profiles/mongodb/README.md
T

6.1 KiB

The mongodb profile

Status (source review 2026-10-21): adapter and migration assets, not a selectable app profile. kit template:prune --profile mongodb is still refused; its error incorrectly says no adapter exists, although @conusai-kit/data-mongo is implemented. The web/worker composition is unfinished. The tables and generated- app steps below describe the target design; repository/transaction and migration tests do not establish a generated application deployment.

The persistence profile for an app whose primary store is MongoDB instead of Postgres (spec §5.1): the same procedures, the same modules, the same migrations discipline, on the official mongodb driver (@conusai-kit/data-mongo) with jobs on BullMQ over Valkey (@conusai-kit/jobs-bullmq).

postgres (default) mongodb (this profile)
Primary store Postgres 18 MongoDB 8.0, single-node replica set (below)
Data adapter @conusai-kit/data-kysely @conusai-kit/data-mongo
Jobs pg-boss (Postgres) BullMQ on Valkey (@conusai-kit/jobs-bullmq)
Tenant wall repository predicate + RLS (three roles, AT-D20) repository predicate + compound indexes leading with org_id — MongoDB has no RLS
Links postgres, identity mongodb, redis, identity
Process env POSTGRES_URL, POSTGRES_READER_URL, POSTGRES_MIGRATE_URL MONGODB_URL, REDIS_URL

The replica_set requirement (AT-D15)

Create the MongoDB service with replica_set set. A managed MongoDB is a standalone server unless replica_set is given at creation (the console's create form offers it and states it cannot be changed afterwards), and a standalone server has no multi-document transactions — a unitOfWork.run on it would be silently non-atomic. @conusai-kit/data-mongo therefore asks the server what it is at boot (hello's setName) and refuses to start against a standalone mongod with a MongoTopologyError that names this setting. A single-node replica set is enough: with replica_set set, the platform starts mongod with --replSet and rs.initiate(), and the injected MONGODB_URL carries directConnection=true. The conusai.json in this folder links mongodb — that link is only valid for an app whose MongoDB service was created this way.

What this folder carries

  • conusai.json — the profile's deployment contract: the same three services, links mongodb + redis + identity. There is no mongodb.migrate flag: the three-role split the postgres profile declares (data-services DS21) exists to make row-level security bind, and MongoDB has none, so every process holds the one MONGODB_URL and ordering is compose's service_completed_successfully alone (AT-D14).
  • compose.yaml, compose.dev.yaml — the profile's compose files. The dev overlay runs mongo:8.0 as a single-node replica set (--replSet plus a one-shot mongo-init that runs rs.initiate()) and valkey/valkey:9-alpine, the images the platform's catalog offers.
  • migrations/ — the kernel collections (tenancy, sign-in, files, proposals, outbox, idempotency keys) as declarative MongoDB commands in JSON — a collection with its $jsonSchema validator generated from the entity's zod schema, and every unique group and index as a compound index leading with org_id — generated by mongoSchemaCommands from the same entity definitions as the postgres files and committed, forward-only. A module's own MongoDB migrations live with the module: modules/<id>/adapters/mongo/migrations/, declared in its manifest as migrations: { postgres: "migrations", mariadb: "...", mongodb: "adapters/mongo/migrations" }.

Target generated-app changes (not wired)

The profile is chosen at generation time (.conusai/template.json profile), and kit template:prune --profile mongodb removes the other dialects. Concretely:

  1. config/src/env.ts reads MONGODB_URL (every process) instead of the three Postgres URLs — one login, because there is no RLS to bind (AT-D20 is Postgres-only).
  2. apps/web and apps/worker compose connectMongoDataSourceFromEnv (which refuses a standalone server, above) and the worker's queue is createBullmqJobQueue/createBullmqWorker over REDIS_URL.
  3. The migrate job runs runMongoMigrationsFromEnv over profiles/mongodb/migrations/ (moved to migrations/mongodb/) and the modules' adapters/mongo/migrations/, each JSON file a mongoCommandsMigration.
  4. This folder's conusai.json, compose.yaml and compose.dev.yaml replace the root ones.

The dialect differences are explicit, never generated at run time (spec §5.3): a module's MongoDB schema is its own numbered folder, reviewed like any migration.

Proof

  • @conusai-kit/data-mongo passes the shared repository and capability contract suites in testcontainers against MongoDB 8 running as a single-node replica set, including a multi-entity UnitOfWork commit and rollback — the tenant predicate is the wall, and the contract is what proves it. Against a standalone mongod the adapter refuses to boot with MongoTopologyError, naming replica_set (AT-D15). The suite's image is mongo:8.2, not mongo:8.0: the 8.0 binary refuses to start on Linux kernels 6.19 and newer (SERVER-121912, the TCMalloc/rseq incompatibility — the refusal is compiled into the server), which covers current development hosts; the topology under test is the same on either minor, and the platform's managed image is mongodb-walg:8.0. The same applies to this folder's compose.dev.yaml on such a kernel: bump its mongo: tag to 8.2.
  • @conusai-kit/jobs-bullmq passes the shared JobQueue contract against valkey/valkey:9 in testcontainers.
  • scripts/vendor/test-template.sh app --profile mongodb runs the template's gate and then applies this folder's and the modules' MongoDB migrations to a real MongoDB 8 container started as a single-node replica set, and probes that the adapter boots on it and refuses a standalone server — so the committed schema is proven against the engine, not just generated.