SQL Mode — New in StrictDB

Write SQL.
Run MongoDB.

The first database driver that executes native SQL against MongoDB. Not a translator — a full execution engine. JOINs, subqueries, window functions, transactions. SQL in, results out.

Your SQL
SELECT u.name, COUNT(o.id) AS orders
FROM users u
INNER JOIN orders o
  ON u.id = o.user_id
WHERE u.status = 'active'
GROUP BY u.name
HAVING COUNT(o.id) > 5
ORDER BY orders DESC
LIMIT 10
StrictDB
MongoDB Pipeline
[
  { $lookup: {
      from: "orders",
      localField: "id",
      foreignField: "user_id",
      as: "o" }},
  { $unwind: "$o" },
  { $match: { status: "active" }},
  { $group: {
      _id: "$name",
      orders: { $sum: 1 }}},
  { $match: { orders: { $gt: 5 }}},
  { $sort: { orders: -1 }},
  { $limit: 10 }
]
npm install strictdb

Three phases. Zero guesswork.

Every SQL query passes through a three-phase pipeline that parses, resolves dependencies, and builds native MongoDB operations.

1

Parse

node-sql-parser

SQL string is parsed into an AST. Tables, columns, joins, subqueries, CTEs, and expressions are extracted and validated.

2

Resolve Dependencies

parallel resolution

Subqueries, CTEs, and correlated expressions are resolved in dependency order. Independent branches run in parallel.

3

Build & Execute

aggregate + bulkWrite

The resolved AST is compiled into MongoDB aggregate pipelines, bulkWrite operations, or multi-stage transactions and executed natively.

The full pipeline
SQL String
    |
    v
[ Parse ] -- node-sql-parser --> AST
    |
    v
[ Resolve ] -- subqueries, CTEs --> resolved values
    |
    v
[ Build ] -- AST + values --> $aggregate / $bulkWrite
    |
    v
MongoDB executes natively
    |
    v
Results returned to you

Other tools translate.
StrictDB executes.

Every other SQL-to-MongoDB tool gives you a query object. You still have to wire execution, handle errors, manage connections. StrictDB does all of it.

QueryLeaf / noql / sql-to-mongo

They translate SQL to a MongoDB query object. Then they hand it to you.

  • Returns a query object — you execute it
  • No connection management
  • No error handling or mapping
  • No transactions
  • No guardrails
  • No JOINs (or severely limited)
  • No window functions
  • No subquery resolution
vs

StrictDB SQL Mode

SQL in. Results out. Nothing in between is your problem.

  • Full execution — results returned directly
  • Connection pooling + auto-reconnect
  • Self-correcting errors with .fix
  • Full transaction support (BEGIN/COMMIT)
  • Guardrails block dangerous queries
  • INNER, LEFT, RIGHT, FULL OUTER JOIN
  • ROW_NUMBER, RANK, DENSE_RANK, LAG, LEAD
  • Subqueries resolved in dependency order

Real SQL. Real pipelines. Real results.

Every example shows the SQL you write, the MongoDB pipeline StrictDB builds, and what you get back. Use { explain: true } to inspect any query.

SELECT + WHERE
SQL Query
const results = await db.sql(`
  SELECT name, email, created_at
  FROM users
  WHERE status = 'active'
    AND age >= 21
  ORDER BY created_at DESC
  LIMIT 20
`);
MongoDB Pipeline (explain)
[
  { $match: {
      status: "active",
      age: { $gte: 21 }
  }},
  { $project: {
      name: 1, email: 1,
      created_at: 1, _id: 0
  }},
  { $sort: { created_at: -1 }},
  { $limit: 20 }
]
INNER JOIN
SQL Query
const results = await db.sql(`
  SELECT u.name, o.total, o.date
  FROM users u
  INNER JOIN orders o
    ON u.id = o.user_id
  WHERE o.total > 100
  LIMIT 50
`);
MongoDB Pipeline (explain)
[
  { $lookup: {
      from: "orders",
      localField: "id",
      foreignField: "user_id",
      as: "o"
  }},
  { $unwind: "$o" },
  { $match: {
      "o.total": { $gt: 100 }
  }},
  { $project: {
      name: 1,
      "o.total": 1,
      "o.date": 1, _id: 0
  }},
  { $limit: 50 }
]
FULL OUTER JOIN
SQL Query
const results = await db.sql(`
  SELECT u.name, p.amount
  FROM users u
  FULL OUTER JOIN payments p
    ON u.id = p.user_id
  LIMIT 100
`);
Two Parallel Pipelines (explain)
// Pipeline 1: LEFT JOIN (users -> payments)
[{ $lookup: { from: "payments", ... }},
 { $unwind: {
     path: "$p",
     preserveNullAndEmptyArrays: true }}]

// Pipeline 2: anti-join (payments not in users)
[{ $lookup: { from: "users", ... }},
 { $match: { "u": { $size: 0 }}}]

// Results merged with $unionWith
SUBQUERY + IN
SQL Query
const results = await db.sql(`
  SELECT name, email
  FROM users
  WHERE id IN (
    SELECT user_id
    FROM orders
    WHERE total > 500
  )
  LIMIT 25
`);
Phase 2: Dependency Resolution
// Phase 2: subquery resolved FIRST
// orders.aggregate([
//   { $match: { total: { $gt: 500 }}},
//   { $project: { user_id: 1 }}
// ]) => [42, 87, 156, ...]

// Phase 3: resolved values injected
[
  { $match: {
      id: { $in: [42, 87, 156, ...] }
  }},
  { $project: {
      name: 1, email: 1, _id: 0
  }},
  { $limit: 25 }
]
GROUP BY + HAVING
SQL Query
const results = await db.sql(`
  SELECT category,
         COUNT(*) AS total,
         AVG(price) AS avg_price
  FROM products
  GROUP BY category
  HAVING COUNT(*) > 10
  ORDER BY avg_price DESC
  LIMIT 20
`);
MongoDB Pipeline (explain)
[
  { $group: {
      _id: "$category",
      total: { $sum: 1 },
      avg_price: { $avg: "$price" }
  }},
  { $match: {
      total: { $gt: 10 }
  }},
  { $project: {
      category: "$_id",
      total: 1,
      avg_price: 1, _id: 0
  }},
  { $sort: { avg_price: -1 }},
  { $limit: 20 }
]
WINDOW FUNCTION — RANK()
SQL Query
const results = await db.sql(`
  SELECT name, department, salary,
    RANK() OVER (
      PARTITION BY department
      ORDER BY salary DESC
    ) AS rank
  FROM employees
  LIMIT 50
`);
MongoDB Pipeline (explain)
[
  { $setWindowFields: {
      partitionBy: "$department",
      sortBy: { salary: -1 },
      output: {
        rank: {
          $rank: {}
        }
      }
  }},
  { $project: {
      name: 1, department: 1,
      salary: 1, rank: 1,
      _id: 0
  }},
  { $limit: 50 }
]
CASE WHEN
SQL Query
const results = await db.sql(`
  SELECT name, salary,
    CASE
      WHEN salary > 100000 THEN 'senior'
      WHEN salary > 60000  THEN 'mid'
      ELSE 'junior'
    END AS level
  FROM employees
  LIMIT 50
`);
MongoDB Pipeline (explain)
[
  { $project: {
      name: 1,
      salary: 1,
      level: {
        $switch: {
          branches: [
            { case: { $gt: ["$salary", 100000]},
              then: "senior" },
            { case: { $gt: ["$salary", 60000]},
              then: "mid" }
          ],
          default: "junior"
        }
      },
      _id: 0
  }},
  { $limit: 50 }
]
INSERT + RETURNING
SQL Query
const result = await db.sql(`
  INSERT INTO users (name, email, role)
  VALUES ('Alice', 'alice@co.io', 'admin')
  RETURNING id, name, created_at
`);
MongoDB Operation (explain)
// 1. insertOne executed
db.users.insertOne({
  name: "Alice",
  email: "alice@co.io",
  role: "admin"
})

// 2. RETURNING fields fetched
// { id: "6614a...", name: "Alice",
//   created_at: "2026-03-29T..." }
PARAMETERIZED QUERY
MySQL-style ? params
const results = await db.sql(
  `SELECT * FROM users
   WHERE role = ? AND age > ?
   LIMIT 10`,
  ['admin', 25]
);
PostgreSQL-style $N params
const results = await db.sql(
  `SELECT * FROM users
   WHERE role = $1 AND age > $2
   LIMIT 10`,
  ['admin', 25]
);
EXPLAIN MODE
Inspect any query
const plan = await db.sql(
  `SELECT name FROM users
   WHERE status = 'active'
   LIMIT 10`,
  [],
  { explain: true }
);

// Returns the MongoDB pipeline
// without executing it
Explain output
{
  sql: "SELECT name FROM users...",
  collection: "users",
  operation: "aggregate",
  pipeline: [
    { $match: { status: "active" }},
    { $project: { name: 1, _id: 0 }},
    { $limit: 10 }
  ]
}

Every SQL construct. Supported.

This is not a subset of SQL. StrictDB supports the full breadth of SQL operations and translates each one to the optimal MongoDB pipeline.

SQL Construct Status MongoDB Translation
Queries
SELECT, WHERE, ORDER BY$match, $sort, $project
LIMIT, OFFSET$limit, $skip
DISTINCT$group
LIKE, NOT LIKE$regex
BETWEEN$gte + $lte
IS NULL, IS NOT NULL$eq: null, $ne: null
IN, NOT IN$in, $nin
Joins
INNER JOIN$lookup + $unwind
LEFT JOIN$lookup + $unwind (preserveNull)
RIGHT JOINReversed $lookup
FULL OUTER JOINTwo pipelines + $unionWith
Aggregation
GROUP BY, HAVING$group + $match
COUNT, SUM, AVG, MIN, MAX$sum, $avg, $min, $max
COUNT(DISTINCT col)$addToSet + $size
Window Functions
ROW_NUMBER()$setWindowFields
RANK(), DENSE_RANK()$setWindowFields
LAG(), LEAD()$setWindowFields + $shift
PARTITION BYpartitionBy field
Subqueries & Expressions
IN (subquery)Pre-resolved, injected as $in
NOT IN (subquery)Pre-resolved, injected as $nin
EXISTS (subquery)Pre-resolved boolean check
CASE WHEN / THEN / ELSE$switch
COALESCE$ifNull
NULLIF$cond + $eq
Mutations
INSERT INTO ... VALUESinsertOne / insertMany
UPDATE ... SET ... WHEREupdateMany
DELETE FROM ... WHEREdeleteMany
RETURNINGPost-mutation findOne
LAST_INSERT_ID()insertedId from receipt
Functions
UPPER, LOWER$toUpper, $toLower
CONCAT$concat
ROUND, ABS, CEIL, FLOOR$round, $abs, $ceil, $floor
LENGTH / CHAR_LENGTH$strLenCP
NOW() / CURRENT_TIMESTAMPnew Date()
Transactions & Parameters
BEGIN / COMMIT / ROLLBACKMongoDB sessions
MySQL ? paramsPositional binding
PostgreSQL $N paramsIndexed binding
{ explain: true }Returns pipeline, no execution

Guardrails still apply.

SQL Mode inherits every guardrail from StrictDB. Dangerous queries are blocked before they reach MongoDB — and errors tell you exactly how to fix them.

🚫

DELETE without WHERE

DELETE FROM users would wipe the entire collection

BLOCKED

SELECT without LIMIT

SELECT * FROM logs would return every document

BLOCKED
🔧

Self-Correcting Errors

Every error includes a .fix field with the exact corrective action

.fix INCLUDED
Self-correcting error
try {
  await db.sql(`DELETE FROM users`);
} catch (err) {
  err.code  // 'GUARDRAIL_BLOCK'
  err.fix   // 'DELETE without WHERE is blocked.
            //  Add a WHERE clause, or use
            //  confirm: "DELETE_ALL" to override.'
  err.retryable // false
}

// AI reads .fix, adds WHERE clause,
// retries successfully

Write SQL. Ship faster.

One install. Full SQL engine on top of MongoDB. Guardrails, explain mode, and self-correcting errors included.

npm install strictdb