Skip to content

vextjs/monSQLize

monSQLize

TypeScript-native MongoDB ODM and enhancement layer with v1-compatible APIs, multi-level caching, distributed locks, transactions, Saga orchestration, model validation, connection pools, Change Stream sync, slow-query logging, and CommonJS / ESM / TypeScript declaration outputs.

npm version License: Apache-2.0 MongoDB Node.js

npm install monsqlize

monSQLize is currently a MongoDB-focused package. The long-term product direction is to keep the MongoDB-style query experience while gradually extending the same high-level API shape to additional database backends.

Table of Contents

Why monSQLize

monSQLize keeps the MongoDB driver mental model while adding the production features most teams end up building around it:

  • Drop-in collection helpers that preserve MongoDB-style CRUD, aggregation, indexes, transactions, and Change Streams.
  • Smart caching through cache-hub, including local memory caching, optional Redis-backed L2 caching, automatic invalidation, and function-level caching.
  • A lightweight Model layer with schema-dsl validation, hooks, relations, populate, custom methods, timestamps, soft delete, and optimistic locking.
  • Multi-connection-pool support, pool health checks, pool-scoped collections/models, and fallback strategies.
  • Business locks and distributed locks for multi-instance deployments.
  • Saga orchestration for multi-step business workflows.
  • Change Stream sync helpers with resume token storage.
  • Slow-query logging and query diagnostics.
  • CommonJS, ESM, and TypeScript declaration outputs from dist/**.

When to Use It

monSQLize is a good fit when you need:

Scenario Benefit
High-concurrency reads Cache hot data and reduce repeated database work.
MongoDB API compatibility Keep familiar query syntax while adding higher-level helpers.
Multi-instance services Use Redis invalidation and distributed locks to keep instances coordinated.
Transaction-heavy flows Use withTransaction() and transaction-aware helpers instead of hand-rolled lifecycle code.
Model-level ergonomics Add schema validation, hooks, populate, and custom methods only where needed.
Smooth upgrade from v1 Keep legacy application source stable while adopting the TypeScript rewrite.

monSQLize is usually not the best first choice for pure write-heavy workloads, extremely strict real-time reads where every query must bypass cache, or very small applications that do not need the extra operational layer.

Installation

npm install monsqlize

Runtime dependencies installed with the package:

  • mongodb - official MongoDB driver.
  • schema-dsl - model schema validation runtime dependency.
  • cache-hub - cache and function-cache foundation.
  • async-lock - local concurrency lock support.

Optional dependencies:

  • ioredis - required only when you enable Redis / L2 cache features.
  • ssh2 - required only when you connect through an SSH tunnel.
npm install ioredis ssh2

Quick Start

CommonJS

const MonSQLize = require('monsqlize');

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'mydb',
  config: {
    uri: 'mongodb://localhost:27017'
  },
  cache: {
    enabled: true,
    ttl: 60_000
  }
});

await db.connect();

const users = db.collection('users');

await users.insertOne({
  username: 'john',
  email: 'john@example.com',
  createdAt: new Date()
});

const user = await users.findOne({ email: 'john@example.com' });
const userById = await users.findOneById('507f1f77bcf86cd799439011');

await users.updateOne(
  { email: 'john@example.com' },
  { $set: { lastLoginAt: new Date() } }
);

await db.close();

ESM and TypeScript

import MonSQLize from 'monsqlize';
import type { Collection } from 'monsqlize';

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'mydb',
  config: {
    uri: 'mongodb://localhost:27017'
  }
});

await db.connect();

const users: Collection = db.collection('users');
const activeUsers = await users.find({ status: 'active' }).toArray();

await db.close();

Published entry points:

Format Entry
CommonJS dist/cjs/index.cjs
ESM dist/esm/index.mjs
Types dist/types/index.d.ts

The package root exports only the public package contract. Deep imports into historical lib/** files are not part of the v2 publishing surface.

Model Layer

The Model layer is optional. Use it when you want schema validation, hooks, relations, populate, custom methods, timestamps, soft delete, or optimistic locking.

schema-dsl is installed automatically as a runtime dependency of monSQLize. You only need to declare schema-dsl in your own app if your application imports it directly.

Manual Model Registration

const MonSQLize = require('monsqlize');
const { Model } = MonSQLize;

Model.define('users', {
  schema: (dsl) => dsl({
    username: 'string:3-32!',
    email: 'email!',
    password: 'string:6-!',
    age: 'number:0-120'
  }),
  relations: {
    posts: {
      from: 'posts',
      localField: '_id',
      foreignField: 'userId',
      single: false
    }
  },
  hooks: (model) => ({
    insert: {
      before: async (ctx, doc) => {
        doc.createdAt = new Date();
        return doc;
      }
    }
  }),
  methods: (model) => ({
    instance: {
      checkPassword(password) {
        return this.password === password;
      }
    },
    static: {
      async findByUsername(username) {
        return model.findOne({ username });
      }
    }
  })
});

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'mydb',
  config: { uri: 'mongodb://localhost:27017' }
});

await db.connect();

const User = db.model('users');
const user = await User.insertOne({
  username: 'john',
  email: 'john@example.com',
  password: 'secret123',
  age: 25
});

Automatic Model Loading

const path = require('path');
const MonSQLize = require('monsqlize');

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'mydb',
  config: { uri: 'mongodb://localhost:27017' },
  models: path.join(__dirname, 'models')
});

await db.connect();

const User = db.model('users');
// models/user.model.js
module.exports = {
  name: 'users',
  schema: (dsl) => dsl({
    username: 'string:3-32!',
    email: 'email!'
  }),
  methods: (model) => ({
    static: {
      async findByUsername(username) {
        return model.findOne({ username });
      }
    }
  })
};

Relative model paths are resolved from process.cwd(). In production services, prefer absolute paths such as path.join(__dirname, 'models').

Populate

Model.define('posts', {
  schema: (dsl) => dsl({
    title: 'string:1-200!',
    content: 'string!',
    userId: 'objectId!'
  })
});

const userWithPosts = await User.findOne({ username: 'john' })
  .populate('posts', {
    select: 'title content',
    match: { status: 'published' },
    sort: { createdAt: -1 },
    limit: 10
  });

Populate is supported by find(), findOne(), findByIds(), findOneById(), findAndCount(), and findPage().

Caching and Performance

monSQLize can cache collection queries and arbitrary async functions.

const users = db.collection('users');

const hotUser = await users.findOne(
  { email: 'john@example.com' },
  { cache: 60_000 }
);
const { withCache } = require('monsqlize');

async function getUserProfile(userId) {
  const user = await db.collection('users').findOneById(userId);
  const orders = await db.collection('orders').find({ userId }).toArray();
  return { user, orders };
}

const cachedGetUserProfile = withCache(getUserProfile, {
  ttl: 300_000,
  cache: db.getCache()
});

await cachedGetUserProfile('user-1');

Cache capabilities include:

  • In-memory L1 cache.
  • Optional Redis-backed L2 cache.
  • Automatic invalidation after writes.
  • Function-level caching through withCache().
  • In-flight request deduplication.
  • Namespaces, TTLs, statistics, and conditional caching.

Advanced Capabilities

Transactions

await db.withTransaction(async (session) => {
  await db.collection('orders').insertOne({ userId, status: 'pending' }, { session });
  await db.collection('users').updateOne(
    { _id: userId },
    { $inc: { orderCount: 1 } },
    { session }
  );
});

Connection Pools

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'main',
  config: { uri: 'mongodb://primary:27017' },
  pools: [
    { name: 'analytics', uri: 'mongodb://analytics:27017' }
  ]
});

const reports = db.pool('analytics').collection('reports');

Distributed Locks

await db.withLock('inventory:sku-1', async () => {
  await db.collection('inventory').updateOne(
    { sku: 'sku-1' },
    { $inc: { stock: -1 } }
  );
});

Change Streams

const watcher = db.collection('orders').watch([
  { $match: { 'fullDocument.status': 'pending' } }
]);

watcher.on('change', (change) => {
  console.log('Order changed:', change.fullDocument);
});

Saga Orchestration

db.defineSaga('checkout', [
  {
    name: 'reserveInventory',
    execute: async (ctx) => reserveInventory(ctx),
    compensate: async (ctx) => releaseInventory(ctx)
  },
  {
    name: 'chargePayment',
    execute: async (ctx) => chargePayment(ctx),
    compensate: async (ctx) => refundPayment(ctx)
  }
]);

await db.executeSaga('checkout', { orderId });

Migration from the MongoDB Driver

The smallest migration is usually to replace only initialization:

const { MongoClient } = require('mongodb');

const nativeClient = await MongoClient.connect('mongodb://localhost:27017');
const nativeUsers = nativeClient.db('mydb').collection('users');
const MonSQLize = require('monsqlize');

const db = new MonSQLize({
  type: 'mongodb',
  databaseName: 'mydb',
  config: { uri: 'mongodb://localhost:27017' },
  cache: { enabled: true }
});

await db.connect();
const users = db.collection('users');

MongoDB-style collection calls can remain unchanged in most cases:

const user = await users.findOne({ email });
const list = await users.find({ status: 'active' }).toArray();

The current v2.0.1 release has been validated against the workspace consumers chat, payment, user, admin, search, vext, and permission-core without requiring business-source changes in those projects.

Compatibility

Surface Current Support
Node.js >=18.0.0; CI covers Node 18 / 20 / 22.
MongoDB driver mongodb@^6.21.0 baseline; driver 7 has additional compatibility coverage.
MongoDB server Memory-server based 6.x / 7.x validation is covered by the project test matrix.
Module systems CommonJS and ESM are both validated.
TypeScript Public declarations are published from dist/types/index.d.ts.
Package license Apache-2.0.

See the current support and verification documents:

Documentation

Current TypeScript documentation and examples are the source of truth for the v2 package:

  • docs/** - current documentation.
  • examples/** - TypeScript examples.
  • test/compatibility/** - package exports and compatibility guards.
  • test/validation/** - verification ledgers and mapping notes.

Historical v1 assets are useful for tracing old behavior, but they are not the current publishing surface for v2.

Development

git clone https://github.com/vextjs/monSQLize.git
cd monSQLize
npm install

Common commands:

npm run build
npm run type-check
npm test
npm run verify:fast
npm run release:preflight

Release preflight runs linting, type checks, size guards, runtime checks, compatibility checks, refactor guards, the default test suite, and npm pack --dry-run.

npm run release:publish runs the preflight gate once and then calls npm publish --ignore-scripts so the final publish step does not repeat the full lifecycle gate. Raw npm publish is still guarded by prepublishOnly.

Optional commands:

npm run test:examples
npm run test:coverage
npm run test:server-matrix
npm run test:real-env:private

test:real-env:private is intentionally opt-in and expects private environment variables. It is not part of the default CI or release gate.

Release Status

The current published release is v2.0.1.

Key release-readiness points:

  • TypeScript rewrite completed for the current runtime and test entry points.
  • Package exports are consolidated under dist/cjs, dist/esm, and dist/types.
  • npm packages include the runtime bundles and declaration files only; source maps are disabled by default and can be generated locally with MONSQLIZE_BUILD_SOURCEMAPS=1 npm run build.
  • v1 smooth-upgrade compatibility has been validated against the target workspace consumers.
  • schema-dsl follows the npm latest TypeScript line ^2.0.3; deprecated 2.3.x mistake releases are intentionally excluded.
  • GitHub Actions publishes to npm from v* tags after running npm run release:preflight; the publish step skips duplicate lifecycle scripts because the gate already ran in the same job.

Roadmap

v2.0.1

  • v1 smooth-upgrade compatibility patch for Model actual collection names, scoped pools/databases, automatic-index dedupe, and cache/pool option aliases.
  • Documentation and public types aligned with the current runtime behavior.

v2.0.0

  • TypeScript-native runtime and declarations.
  • v1 smooth-upgrade compatibility bridge.
  • Multi-level cache and function-cache support through cache-hub.
  • Transactions, business locks, distributed locks, Saga orchestration, connection pools, Change Stream sync, and slow-query logging.
  • Model layer with schema-dsl validation, relations, populate, hooks, and custom methods.

v2.x

  • Query analyzer improvements.
  • Automatic index suggestions.
  • Migration tooling.
  • GraphQL integration experiments.
  • More real-environment validation coverage.

v3.0+

  • Unified API experiments for MySQL.
  • Unified API experiments for PostgreSQL.
  • Broader ORM capabilities.
  • Cross-database sync middleware.

License

monSQLize is released under the Apache License 2.0.

Support

About

monSQLize is a universal query adapter that converts various query languages (e.g., SQL for MySQL, PostgreSQL) into MongoDB syntax. It dynamically translates familiar query styles into MongoDB-compatible operations, reducing complexity, saving effort, and improving efficiency, so developers can focus on application logic.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors