nodejs-store

MongoDB to PostgreSQL migration

The problem

A service started on MongoDB and now needs PostgreSQL (compliance, reporting, a new team’s tooling — the reason does not matter). The obvious cost is rewriting every query: Mongo aggregations, $lookups and a pile of driver-specific code, all of which must be re-expressed in SQL, then re-tested. You also want to see the target’s physical structure (types, nullability) to plan the move, without a migration tool rewriting anything behind your back.

During the transition you may run both databases side by side — a new read path on PostgreSQL, the old one still on MongoDB — so both must speak the same query language.

Why nodejs-store

Walkthrough

const { init, store } = require('nodejs-store');

// One schema definition — no backend-specific fields.
const Order = {
  name: 'Order',
  collection: 'orders',
  idPrefix: 'OD',
  timestamps: true,
  fields: {
    title: { type: 'string', default: '' },
    status: { type: 'string', default: 'draft' },
    amount: { type: 'float', default: 0 },
    createdBy: { type: 'string' },
  },
  computes: {
    total: { type: 'float', depends: ['amount'], fn: (d) => d.amount * 1.1 },
  },
  indexes: [{ keys: { status: 1, createdAt: -1 } }],
  read: ['editor', 'viewer'],
  write: ['editor'],
};
store.register(Order);

// The same GQL string for both backends.
const gql = 'Order($condition:@c0,$sort:@s0){ _id, title, status, total }';
const params = { c0: { status: 'open' }, s0: { createdAt: -1 } };

// Today: MongoDB (native aggregation).
await init({ default: mongoDb });
const fromMongo = await store.query(gql, params);

// Tomorrow: PostgreSQL — only the datasource changes; the GQL above is unchanged.
await init({ default: { kind: 'postgres', exec: pgPool } });
const fromPostgres = await store.query(gql, params);

To inspect the PostgreSQL structure and merge your local defs on top:

const { syncSchema } = require('nodejs-store'); // same function as store.syncSchema

const defs = await store.syncSchema({
  backend: 'postgres',
  driver: pgPool,        // prefer a read-only account
  overlay: [Order],      // local permissions / computes / overrides merged on top
  datasource: 'pg_a',
});
// defs: merged schemaJSON[] — introspection only, no DDL is written back.

Pitfalls

See also