@mostajs stack, end to end (a real
migration)The capstone of the
@mostajs/ormecosystem. Not a toy: this walks FitZoneGym, a real 40-model Prisma app, through the whole stack — one thread, oneEntitySchema.
We take a real Prisma app — FitZoneGym, 40 models — and, without rewriting application code, turn it into a system that:
@mostajs/orm-cli
+ @mostajs/orm,least-lag routing and failover — via @mostajs/replicator,@mostajs/replica-monitor,@mostajs/orm-copy-data.Steps 1–4 are wired in FitZoneGym today; step 5 is the natural next addition. Five packages, one schema model, no second mental model anywhere.
FitZoneGym is a production-shaped Next.js + Prisma app — 40
models, dozens of new PrismaClient(...) call
sites, originally on MongoDB. It works, until the requirements arrive:
the deployment target is Postgres, the team wants a read replica, and
ops wants eyes on it. With stock Prisma each of those is a project. With
@mostajs, each is a step — and they all share one fact:
everything is the same @mostajs/orm
EntitySchema.
One command migrated FitZoneGym onto @mostajs/orm:
cd FitZoneGym
npx @mostajs/orm-cli bootstrap # codemod + install + convert + DDLThe codemod rewrote every new PrismaClient(...) site to
a Prisma-compatible client from @mostajs/orm-bridge
(originals saved as *.prisma.bak, fully reversible),
converted prisma/schema.prisma to
.mostajs/generated/entities.json (40 entities), and applied
DDL. Then we pointed it at Postgres:
# .mostajs/config.env
DB_DIALECT=postgres
SGBD_URI=postgresql://devuser:***@localhost:5432/fitzonedbApplication code edited by hand: 0 — the same
db.user.findMany(...) calls now run on Postgres. (Full
story: the orm-cli
deep-dive.) We now own a 13-database-ready
schema.
Here’s where it gets interesting. FitZoneGym’s real topology is a
Postgres master with a SQLite slave — replication
across dialects, set up with @mostajs/replicator:
import { ReplicationManager } from '@mostajs/replicator'
import { ProjectManager } from '@mostajs/mproject'
const rm = new ReplicationManager(new ProjectManager())
await rm.addReplica('fitbymongo', { name: 'master-pg', role: 'master',
dialect: 'postgres', uri: 'postgresql://devuser:***@localhost:5432/fitzonedb',
pool: { min: 2, max: 20 } })
await rm.addReplica('fitbymongo', { name: 'slave', role: 'slave',
dialect: 'sqlite', uri: './fitzonedb.db', lagTolerance: 5000 })
rm.setReadRouting('fitbymongo', 'least-lag') // reads go to the freshest nodeThat’s the differentiator no other JS/TS ORM ships: the
master is Postgres, the replica is SQLite — a durable
embedded mirror you can ship, query offline, or fail over to.
await rm.promoteToMaster('fitbymongo', 'slave') promotes
it; lagTolerance: 0 on a critical replica blocks promotion
while it’s behind, so failover never silently loses data.
FitZoneGym also defines CDC rules — incremental
change capture on selected collections, idempotent
(source-wins):
rm.addReplicationRule({
name: 'cdc-fitbymongo', source: 'fitbymongo', target: 'fitbymongo',
mode: 'cdc', collections: ['users', 'clients'], conflictResolution: 'source-wins',
})
rm.enableAutoPersist('.mostajs/replicator-tree.json') // persist topology for the monitorThe honest contract (spelled out in the replicator deep-dive):
CDC is eventually consistent and at-least-once, so rules are idempotent
— and @mostajs/replicator can replay captured changes
onto a different dialect when the target is another
store, which is how you’d feed an analytics database from the Postgres
primary.
Step 3 persisted the topology to replicator-tree.json,
so the dashboard needs no database connection — it
reads that file (credentials are already masked, see the
*** above) and streams live status. FitZoneGym ships a
services/monitor.mjs; under the hood:
npx mostajs-monitor --tree .mostajs/replicator-tree.json --port 14499 --token SECRET
# http://127.0.0.1:14499 — master/slave roles, live lag + sparkline, CDC rule stats, activity feed“Is the SQLite slave keeping up? did cdc-fitbymongo
run?” becomes a glance. Zero external dependencies — pure
node:http + vanilla JS. (the replica-monitor
deep-dive.)
Replication is not backup. FitZoneGym doesn’t wire this yet — and
that’s the honest state — but it’s one config away. A nightly job would
read the primary once and write a SQLite copy + a SQL
dump + a JSON archive, with @mostajs/orm-copy-data:
# backup-daily.conf
--source db --source-dialect postgres --source-uri "postgresql://devuser:pass@localhost:5432/fitzonedb"
--dest db --dest-dialect sqlite --dest-uri "./backups/daily.sqlite"
--dest sql-dump --dest-file "./backups/daily.sql"
--dest json --dest-file "./backups/daily.json"
--schemas .mostajs/generated/entities.json --create-tables --batch-size 1000# /etc/cron.d/fitzone-backup
0 2 * * * cd /app && npx mostajs-copy --commandFile backup-daily.conf >> /var/log/backup.log 2>&1The --schemas .mostajs/generated/entities.json is the
same file the bootstrap produced in Step 1 — one source
of truth, from migration to backup.
Prisma app (FitZoneGym, 40 models)
│ orm-cli bootstrap (every PrismaClient site rewritten, 0 by hand)
▼
@mostajs/orm ── Postgres primary (master-pg)
│
replicator ── SQLite replica (slave, least-lag) + CDC rules (users/clients)
│
replica-monitor ── reads replicator-tree.json (no DB) ── live lag / rules / activity
┊
orm-copy-data ── nightly fan-out backup (the next step)
What ties it together isn’t glue code — it’s that every box
is the same @mostajs/orm
EntitySchema. The 40 entities you migrated are the
entities you replicate, monitor and (next) back up. One model, learned
once, all the way down.
You don’t adopt all five at once — each stands alone, they compose when needed:
@mostajs/orm-cli →
@mostajs/orm.@mostajs/replicator
+ @mostajs/replica-monitor.@mostajs/orm-copy-data.npx @mostajs/orm-cli bootstrap # in your Prisma project — the on-ramp@mostajs/orm — 13
databases, one API, zero codegenIf a database stack that grows with you — without a second mental model — is useful, a ⭐ on the repos helps. It’s the signal AI dev tools use to surface the packages.
Tags: #nodejs #typescript
#database #prisma #migration
#replication #cdc #cqrs
#backup #postgresql #sqlite
#orm #mostajs
Auteur : Dr Hamid MADANI drmdh@msn.com