MongoDB
LimitedCreate or configure a Ridu project for the supported MongoDB replica-set deployment.
Choose MongoDB when you can run the supported replica-set topology below. Collections, access rules, REST, the generated SDK, and the admin work the same way as they do with Ridu’s other database adapters; application code does not use MongoDB filters or driver types.
Production support is limited to:
- an ordinary Ridu-generated
starterorblankproject; - Linux x86-64;
- MongoDB Community 8.2.9, with release evidence built from digest-pinned images;
- SCRAM-SHA-256 authentication and TLS with CA and hostname verification; and
- a writable three-member replica set.
This does not include Atlas, Amazon DocumentDB, Azure Cosmos DB, a standalone mongod, another
MongoDB version or topology, another operating system or architecture, arbitrary scale,
network-partition/failover matrices, or point-in-time recovery. See
Releases and compatibility before choosing this profile.
Create a project
npm create ridu@latest my-appbun create ridu@latest my-apppnpm create ridu@latest my-appyarn create ridu my-appChoose Starter and MongoDB in the wizard, then run the exact install and dev commands it
prints. Review the summary carefully: MongoDB is not the default database choice.
Preselect every scaffold choice
For a non-interactive npm setup, pass every choice as an argument:
npm create ridu@latest -- \
--template starter \
--database mongodb \
--module github.com/acme/content \
--scope @acme \
--package-manager npm \
--agent codex \
contentBoth generated templates include the same adapter wiring. The dev script starts the pinned local
single-node replica set from compose.yaml and uses:
mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0This convenient plaintext, unauthenticated service is for local development only. It is still a
replica set because Ridu operations require MongoDB transactions; a standalone mongod is
rejected. To use another development replica set without putting its URL in process arguments,
supply a command-scoped environment value from your shell’s secret binding:
DATABASE_URL="$MONGODB_DEVELOPMENT_URL" npm run dev -- --no-dockerDATABASE_URL="$MONGODB_DEVELOPMENT_URL" bun run dev -- --no-dockerDATABASE_URL="$MONGODB_DEVELOPMENT_URL" pnpm run dev --no-dockerDATABASE_URL="$MONGODB_DEVELOPMENT_URL" yarn run dev --no-dockerConfiguration discovery, generate, generate --check, and migrate create through the
project-local CLI remain offline. They do not open MongoDB or serialize its URL into config,
generated contracts, or migration artifacts.
Add MongoDB to an existing project
Use this path for a clean generated Ridu project before it has committed migration history or live data. Ridu has no generic PostgreSQL/SQLite-to-MongoDB data migration facility. Moving a live dataset is application-owned: export and transform every current document, localized value, version, auth/session record, relationship, task, preference, lock, and upload reference; preserve stable IDs; validate the destination; and rehearse backup, cutover, and rollback.
1. Select MongoDB in project metadata
Replace the existing adapter value. This example starts from PostgreSQL:
version = 1
database = "postgres"
database = "mongodb"
entry = "./cmd/server"
admin = "./admin"The value must match the CLI’s public adapter name. It controls development orchestration and migration routing; it does not contain a connection URL.
2. Use the generated adapter handshake
MongoDB development needs more than replacing one Open call. The internal candidate started by
ridu dev must verify the resolved manifest’s exact indexes before the stable proxy promotes it.
Copy the following generated pattern into cmd/server/main.go (retaining the project’s admin asset
and handler options):
import (
"context"
"fmt"
"log"
"os"
"github.com/acme/content/content"
"github.com/acme/content/internal/adminassets"
"github.com/riducms/ridu"
"github.com/riducms/ridu/adapters/postgres"
"github.com/riducms/ridu/adapters/mongodb"
"github.com/riducms/ridu/store"
)
const internalDevelopmentServerArgument = "--ridu-internal-development-server"
func main() {
applicationConfig := content.Config()
internalDevelopmentServer := len(os.Args) == 2 && os.Args[1] == internalDevelopmentServerArgument
if internalDevelopmentServer {
os.Args = os.Args[:1]
}
var options []ridu.ExecuteOption
if len(os.Args) == 1 {
options = runtimeOptions(applicationConfig)
options = runtimeOptions(applicationConfig, internalDevelopmentServer)
}
if err := ridu.Execute(applicationConfig, options...); err != nil {
log.Fatal(err)
}
}
func runtimeOptions(applicationConfig ridu.Config) []ridu.ExecuteOption {
func runtimeOptions(applicationConfig ridu.Config, internalDevelopmentServer bool) []ridu.ExecuteOption {
return []ridu.ExecuteOption{
ridu.WithStore(func(ctx context.Context) (store.Store, error) {
return postgres.OpenWithConfig(ctx, postgres.PoolConfig{
DatabaseURL: os.Getenv("DATABASE_URL"),
AllowInsecureTransport: envBool("RIDU_ALLOW_INSECURE_DATABASE"),
MaxUploadLockConnections: envInt32("RIDU_POSTGRES_UPLOAD_LOCK_CONNECTIONS"),
})
backend, err := mongodb.OpenWithConfig(ctx, mongodb.Config{
DatabaseURL: os.Getenv("DATABASE_URL"),
AllowInsecureTransport: envBool("RIDU_ALLOW_INSECURE_DATABASE"),
ApplicationName: "ridu-server",
})
if err != nil {
return nil, err
}
if internalDevelopmentServer {
manifest, err := ridu.Resolve(applicationConfig)
if err != nil {
_ = backend.Close()
return nil, fmt.Errorf("resolve MongoDB development manifest: %w", err)
}
if err := backend.VerifyIndexes(ctx, manifest); err != nil {
_ = backend.Close()
return nil, fmt.Errorf("verify MongoDB development indexes: %w", err)
}
}
return backend, nil
}),
ridu.WithAddress(env("RIDU_ADDRESS", ":8080")),
ridu.WithHandlerOptions(ridu.HandlerOptions{
AdminAssets: adminassets.FS(),
AllowedOrigins: envList("RIDU_ALLOWED_ORIGINS"),
AllowedHosts: envList("RIDU_ALLOWED_HOSTS"),
TrustedProxyCIDRs: envList("RIDU_TRUSTED_PROXY_CIDRS"),
ReadinessTimeout: envDuration("RIDU_READINESS_TIMEOUT"),
StrictTransportSecurity: os.Getenv("RIDU_STRICT_TRANSPORT_SECURITY"),
}),
ridu.WithServerOptions(ridu.ServerOptions{
ShutdownTimeout: envDuration("RIDU_SHUTDOWN_TIMEOUT"),
WorkerDrainTimeout: envDuration("RIDU_WORKER_DRAIN_TIMEOUT"),
ReadinessDrainDelay: envDuration("RIDU_READINESS_DRAIN_DELAY"),
AllowUnverifiableReadiness: envBool("RIDU_ALLOW_UNVERIFIABLE_READINESS"),
SkipReadinessPreflight: envBool("RIDU_SKIP_READINESS_PREFLIGHT"),
AllowUnverifiableReadiness: internalDevelopmentServer,
SkipReadinessPreflight: internalDevelopmentServer,
}),
}
}The red PostgreSQL lines are context from the existing generated server. Remove them after adding
the MongoDB lines; the finished file has one adapter import, one runtimeOptions call and
declaration, one store factory, and one value for each readiness option.
The readiness exceptions are only for the CLI’s internal development candidate, after the
explicit index check. Do not expose them as production environment fallbacks. Keep the template’s
envBool helper so invalid environment values fail clearly, and close the backend on every
resolution/verification error as shown.
3. Add the local replica-set service
Copy this service into compose.yaml:
services:
mongodb:
image: mongo:8.2.9-noble@sha256:007773db61cb1aa44e526fb7175fc582902e67d4e6cc5f13106445767d46c818
command: ['mongod', '--replSet', 'ridu-rs0', '--bind_ip_all']
ports:
- '127.0.0.1:27029:27017'
healthcheck:
test:
- CMD-SHELL
- >-
mongosh --quiet --eval 'try { const h=db.hello(); if (h.setName === "ridu-rs0" && h.isWritablePrimary) { quit(0) }; if (!h.setName) { try { rs.initiate({_id:"ridu-rs0",members:[{_id:0,host:"mongodb:27017"}]}) } catch (_) {} } } catch (_) {}; quit(1)'
interval: 2s
timeout: 5s
retries: 30
start_period: 5sThe health check initializes ridu-rs0 and succeeds only when the node is its writable primary.
Ridu requires sessions and transactions, so a standalone MongoDB server is not sufficient. The
local URL is:
mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0This single-node, unauthenticated, plaintext service is development-only and is not the three-member authenticated TLS production profile.
4. Start and verify development
go mod tidy
npm run devgo mod tidy
bun run devgo mod tidy
pnpm run devgo mod tidy
yarn run devridu dev starts the generated replica-set service, regenerates contracts, synchronizes safe
additive changes, and starts the API and admin. Before deployment, create and verify the immutable
migration history:
export DATABASE_URL='mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0'
export RIDU_ALLOW_INSECURE_DATABASE=true
npm run ridu -- migrate create --name initial
npm run ridu -- migrate plan
npm run ridu -- migrate verify
npm run ridu -- migrate up
npm run ridu -- migrate statusexport DATABASE_URL='mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0'
export RIDU_ALLOW_INSECURE_DATABASE=true
bun run ridu -- migrate create --name initial
bun run ridu -- migrate plan
bun run ridu -- migrate verify
bun run ridu -- migrate up
bun run ridu -- migrate statusexport DATABASE_URL='mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0'
export RIDU_ALLOW_INSECURE_DATABASE=true
pnpm run ridu migrate create --name initial
pnpm run ridu migrate plan
pnpm run ridu migrate verify
pnpm run ridu migrate up
pnpm run ridu migrate statusexport DATABASE_URL='mongodb://127.0.0.1:27029/ridu?directConnection=true&replicaSet=ridu-rs0'
export RIDU_ALLOW_INSECURE_DATABASE=true
yarn run ridu migrate create --name initial
yarn run ridu migrate plan
yarn run ridu migrate verify
yarn run ridu migrate up
yarn run ridu migrate statusFor a separately managed development replica set, omit the Compose service and rerun the selected
tab’s dev --no-docker command with DATABASE_URL="$MONGODB_DEVELOPMENT_URL". Keep TLS and
authentication enabled unless the target is a disposable local development database. Before
production, replace the local URL and insecure admission with the authenticated, CA- and
hostname-verified three-member profile below and follow the full release cutover.
Development schema changes
During ridu dev, an additive manifest change creates only missing collections and indexes. A
separate serving Store then non-mutatingly verifies the exact physical index plan and passes
development readiness before the stable proxy promotes the candidate. A failed build, incompatible
index, unhealthy candidate, or rejected rename leaves the last working process in place.
Development synchronization never drops state or guesses how stored content should move. For a rename, compiled transform, index replacement, or reviewed retirement, create and apply an immutable production migration instead of repairing MongoDB by hand.
--no-sync skips additive mutation, but it does not weaken verification: the candidate starts only
when another owner has already prepared the selected database.
Production connection and credentials
The production DATABASE_URL must select the application database, authenticate with
SCRAM-SHA-256, name the replica set, and enable certificate and hostname verification. For example:
mongodb://ridu-app:<secret>@mongo-1.example.internal,mongo-2.example.internal,mongo-3.example.internal/content?authSource=content&authMechanism=SCRAM-SHA-256&replicaSet=ridu-rs0&tls=true&tlsCAFile=/run/secrets/mongodb-ca.pemKeep the running application credential scoped to its database. Use separate, short-lived credentials for verification and backups:
| Credential | Scope |
|---|---|
| Application | Database-scoped access to the Ridu database. Use it for the running app and, when sufficient, plan, up, and status. |
| Verification | Permission to create and drop the temporary database used by migrate verify. |
| Backup | The database-scoped permissions required by mongodump and mongorestore. |
Supply each credential only to its command. Ridu redacts credentials and topology details from normal
connection failures, but operators must still keep URLs out of shell history, process arguments,
logs, generated files, and image layers. A mode-0600 Database Tools configuration file is one way
to keep backup credentials out of process arguments.
RIDU_ALLOW_INSECURE_DATABASE and --allow-insecure-database are for local development only.
They do not expand the production support profile.
Immutable migration lifecycle
Artifacts created by ridu migrate create use MongoDB planner contract 2.0.0 inside shared
artifact-envelope format 1.
Authenticated planner-1.0.0 artifacts remain a supported immutable prefix: the runner validates
and replays that committed history before applying v2 artifacts rather than rewriting or rejecting
it. Create and inspect the plan before the cutover:
npm run ridu -- migrate create --name add-post-summary
npm run ridu -- migrate plan --jsonbun run ridu -- migrate create --name add-post-summary
bun run ridu -- migrate plan --jsonpnpm run ridu migrate create --name add-post-summary
pnpm run ridu migrate plan --jsonyarn run ridu migrate create --name add-post-summary
yarn run ridu migrate plan --jsonRidu binds each artifact to its manifest history and rejects altered or reordered migration files.
up takes a fenced lease with a bounded wait, records durable
step progress, and resumes the same immutable history after an interruption. It requires explicit
maintenance admission only when the pending or incomplete history suffix contains a rename,
transform, reference-index rebuild, or resource retirement. Run that work only after every old
application process and worker is drained.
verify creates a random isolated database, replays the complete history with the adapter’s
migration runner, checks the final ledger and index state, and drops that database. Because the
temporary database starts empty, verify requires --allow-maintenance whenever any artifact in
the complete history contains semantic work, including work already applied to the live database.
status is non-mutating.
ridu build embeds a fingerprint of the exact ordered migration filenames and artifact digests.
Application readiness requires the live ledger to match that fingerprint, its head to match the
executable manifest, and every required Ridu index to pass non-mutating verification.
ridu check and ridu build remain offline and fail when committed history does not end at
executable config.
A direct go build has no fingerprint and fails closed in ordinary production startup. Deployment
cutover additionally fails when the applied ledger or physical indexes do not exactly match the
release history.
MongoDB does not expose down, reset, refresh, or fresh. Correct forward with another
reviewed artifact, or restore the complete matched recovery point.
Deploy and recover
For a code-only replacement with the same manifest digest and migration-history fingerprint, start
the candidate, require /readyz, then drain the old process. For any new migration artifact—even an
additive or same-manifest data-only one—use the coordinated sequence:
- Rehearse the exact binary and history against a restored recovery point.
- Drain every old application process and worker.
- Run
migrate verifythrough the project-local CLI withDATABASE_URL="$MONGODB_OPERATIONAL_URL"so the shadow-database authority exists only for that command. Append--allow-maintenancewhenever the complete committed history contains semantic work. - After verification succeeds, capture one recovery point containing a database-scoped
mongodumpand the upload store. Use a separately scoped$MONGODB_BACKUP_URL. - Run
migrate upthrough the project-local CLI withDATABASE_URL="$MONGODB_MIGRATION_URL", using the selected database-scoped application or controlled operator identity. Append--allow-maintenanceonly when the pending or incomplete history suffix contains semantic work. - Run
migrate statuswith the same migration identity and require exact complete history and Ridu-managed index state. - Start only the target release binary carrying the expected manifest and migration-history
fingerprints with
$MONGODB_APP_URL, then wait for/readyzbefore admitting traffic.
Take the cutover recovery point only after draining writers and successfully completing the
command-scoped verification, but before up. The recovery drill uses database-scoped
mongodump --archive --gzip --dumpDbUsersAndRoles, restores that archive into an empty target with
mongorestore --archive --gzip --drop --restoreDbUsersAndRoles, and restores upload storage from
the same named point. The dump/restore must remain scoped to the selected application database;
never restore over unrelated databases. Confirm the ledger and status, the restored scoped user,
auth and sessions, content and versions, relationships, SDK/admin workflows, rich text, upload
objects, tasks, and representative data before directing traffic.
This is a logical dump/restore path, not point-in-time recovery. Define and rehearse your retention, encryption, off-site storage, recovery-point objective, and recovery-time objective.
Test the deployment
Before production, rehearse migrations, startup, readiness, a primary election, graceful shutdown, and database/upload restoration on the topology you will deploy. Exercise authentication, content, versions, relationships, uploads, tasks, the SDK, and the admin after restoration. Use disposable databases for these tests; never point them at production.