querymesh
v0.1.0
Published
Lightweight multi-dialect query builder and ORM for Node.js (PostgreSQL, MySQL, SQL Server, Oracle, MongoDB).
Maintainers
Readme
QueryMesh
QueryMesh is a compact query builder + light ORM for Node.js with multi-database support.
HTML documentation: overview and API reference
Supported dialects
- PostgreSQL (
pg) - MySQL (
mysql) - SQL Server (
mssql) - Oracle (
oracledb) - MongoDB (
mongo,mongodb,mongoose) — same fluent builder, but executed as real MongoDB operations.
Cross-dialect support (what is not universal)
| Feature | pg | mysql | mssql | oracle | mongo |
|---|---|---|---|---|---|
| Basic query builder (where, NOT, IS, NULL, MIN/MAX) | ✅ | ✅ | ✅ | ✅ | ✅ |
| UNION / UNION ALL | ✅ | ✅ | ✅ | ✅ | ✅ (QueryBuilder sources) |
| INSERT ... SELECT | ✅ | ✅ | ✅ | ✅ | ✅ (QueryBuilder source + explicit select list) |
| ANY/ALL with subquery/raw source | ✅ | ✅ | ✅ | ✅ | ❌ |
| ANY/ALL with literal array | ✅ | ✅ | ✅ | ✅ | ✅ |
| .onConflictDoUpdate(...) | ✅ | ❌ | ✅ (single-row insert) | ✅ (single-row insert) | ✅ (single-row insert) |
| .onDuplicateKeyUpdate(...) | ❌ | ✅ | ❌ | ❌ | ❌ |
| .returning(...) | ✅ (native) | ❌ | ✅ (native OUTPUT) | ❌ | ✅ (best-effort on mutations) |
| schema().createView()/dropView() | ✅ | ✅ | ✅ | ✅ | ❌ |
| Triggers (createTrigger/dropTrigger) | ✅ | ✅ | ✅ | ✅ | ❌ |
| Schema DDL (createTable/alterTable/dropTable) | ✅ | ✅ | ✅ | ✅ | ⚠️ (dropTable only) |
| Maintenance (truncateTable) | ✅ | ✅ | ✅ | ✅ | ✅ (deleteMany({})) |
| Maintenance (analyzeTable) | ✅ | ✅ | ✅ | ✅ | ❌ |
| Maintenance (optimizeTable) | ✅ (VACUUM) | ✅ | ✅ | ❌ | ❌ |
| Maintenance (vacuumTable / vacuumDatabase) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Maintenance (reindexTable) | ✅ | ❌ | ✅ | ❌ | ❌ |
| Maintenance (repairTable) | ❌ | ✅ | ❌ | ❌ | ❌ |
Mongo notes:
- Mongo joins use
$lookup; comparison join operators are supported (=,!=,<>,>,>=,<,<=). - Mongo
UNION/UNION ALLsupports QueryBuilder sources (raw/string SQL sources are not supported). - Mongo
insertSelectsupports QueryBuilder source with explicit selected columns. - Mongo
schema().dropTable(name)drops a collection;createTable/alterTableremain SQL-only. - Mongo
schema().truncateTable(name)deletes all documents withdeleteMany({}). - Mongo triggers are not supported by QueryMesh (
createTrigger/dropTriggerare SQL dialect features). - Right/full join semantics are not fully equivalent to SQL joins in Mongo pipelines.
- For Mongo schema APIs, use
showTables,showDatabases, andgetDescfor introspection. optimizeTableis a best-effort mapping, not identical SQL across dialects: pg usesVACUUM, mysql usesOPTIMIZE TABLE, mssql usesALTER INDEX.- PostgreSQL
VACUUMhelpers cannot run inside a transaction block.
Install
npm i querymesh
# install only the driver(s) you need
npm i pg
npm i mysql
npm i mssql
npm i oracledb
npm i mongodbConnect
import QueryMesh from "querymesh";
// PostgreSQL
const db = await QueryMesh.connect({
dialect: "pg",
config: { connectionString: process.env.DATABASE_URL },
// also accepts { server, user, password, database } (server -> host alias)
// and supports config.options to pass Pool options:
// config: { server: "localhost", user: "u", password: "p", database: "app", options: { ssl: true, max: 20 } }
});
// MySQL
// const db = await QueryMesh.connect({
// dialect: "mysql",
// config: {
// server: "127.0.0.1", // preferred (alias to host)
// user: "root",
// password: "",
// database: "app",
// port: 3306,
// options: { connectTimeout: 10000, multipleStatements: true },
// },
// });
// SQL Server
// const db = await QueryMesh.connect({
// dialect: "mssql",
// config: { server: "localhost", user: "sa", password: "pass", database: "master", options: { trustServerCertificate: true } },
// });
// Oracle
// const db = await QueryMesh.connect({ dialect: "oracle", config: { user, password, connectString } });
// MongoDB
// const db = await QueryMesh.connect({
// dialect: "mongo", // alias: "mongodb"
// config: {
// // any of these forms work:
// connectionString: "mongodb://localhost:27017/app",
// // uri: "mongodb://localhost:27017/app",
// // or host/server + optional port + db:
// // server: "localhost", port: 27017, database: "app",
// options: { maxPoolSize: 20, serverSelectionTimeoutMS: 5000 }, // alias of clientOptions
// },
// });
// Mongoose (reuse existing mongoose connection)
// import mongoose from "mongoose";
// await mongoose.connect("mongodb://localhost:27017/app");
// const db = await QueryMesh.connect({
// dialect: "mongoose",
// config: { mongoose }, // or { connection: mongoose.connection }
// });Pool and connection behavior
- Create one shared
DBinstance withQueryMesh.connect(...)and reuse it. Do not open a new connection per request. pg,mysql,mssql, andoraclecreate and manage a driver pool internally.mongocreates aMongoClient; MongoDB pooling is handled by the client internally.mongoosereuses the existing Mongoose/Mongo connection instead of creating a second Mongo client.- Query builder calls (
db.table(...).get()/run()) and raw SQL calls (db.query(...)) use the same underlying pool/client. db.transaction(...)checks out a dedicated connection/session for the transaction, then releases it after commit or rollback.db.close()closes the underlying pool/client.- Existing SQL pools are not accepted directly by
connect(...)yet. Existing Mongodb/ Mongoose connections are supported.
Switch database
// switch existing DB handle from A -> B
await db.switchDatabase("app_b");
// alias
await db.useDatabase("app_c");Notes:
pg,mysql,mssql: opens a new pool using the same config with updated database name.mongo: reuses the same MongoClient and switchesdbhandle.oracle: reconnects with the provided target asconnectString.
Switch dialect
// switch this same DB instance from one dialect to another
await db.switchDialect("mongo", {
connectionString: "mongodb://localhost:27017/app_b",
});
// alias + explicit options
await db.useDialect(
"pg",
{ server: "localhost", user: "u", password: "p", database: "app_b" },
{
closeCurrent: true, // default true
// features/importer are optional overrides
}
);Notes:
- Reuses the same
DBobject and swaps its adapter. - By default the previous adapter is closed; set
closeCurrent: falseto skip that. importerandfeaturescan be passed to override runtime driver loading.
Mongo ObjectId helper
Use toObjectId(...) to safely convert values for Mongo _id/reference filters.
import QueryMesh, { toObjectId } from "querymesh";
const id = await toObjectId("507f1f77bcf86cd799439011");
const row = await db.table("users").where("_id", id).first();Sync variant (when you already have ObjectId constructor):
import { ObjectId } from "mongodb";
import { toObjectIdSync } from "querymesh";
const id = toObjectIdSync("507f1f77bcf86cd799439011", ObjectId);whereIn example:
const ids = await Promise.all(rawIds.map((v) => toObjectId(v)));
const rows = await db.table("users").whereIn("_id", ids).get();Extended JSON is supported:
const id = await toObjectId({ $oid: "507f1f77bcf86cd799439011" });Notes:
- If value is already an ObjectId-like instance,
toObjectIdreturns it unchanged. - If
mongodbdriver is missing, the helper throws install guidance (npm install mongodb). toObjectIdSyncdoes not import anything; you must passObjectIdconstructor explicitly.
Query builder
Select
const users = await db
.table("users")
.select(["id", "email"])
.where("is_active", true)
.orderBy("id", "desc")
.limit(20)
.get();Where + OR
const rows = await db
.table("users")
.where("country", "GN")
.orWhere("role", "admin")
.get();Raw SQL fragments
Use raw(...) when you need a SQL expression that QueryMesh should not quote as a column or bind as a normal value.
import { raw } from "querymesh";
const user = await db
.table("users")
.where(raw("LOWER(email)"), "[email protected]")
.first();
await db
.table("posts")
.update({ updated_at: raw("CURRENT_TIMESTAMP") })
.where("id", postId)
.run();Notes:
- Use
raw(...)sparingly and only with trusted SQL fragments. - Keep user input as normal values (
where("email", value)) so QueryMesh can bind it safely. raw(...)is for SQL dialects; it is not a Mongo query expression helper.
Full SQL query
Use db.query(...) when you want to run a full SQL statement directly. It supports SELECT, INSERT, UPDATE, DELETE, and DDL statements for SQL dialects.
// Portable placeholders use ? and QueryMesh rewrites them for the current dialect.
const result = await db.query(
"SELECT id, email FROM users WHERE id = ?",
[userId]
);
console.log(result.rows); // [{ id, email }]
console.log(result.rowCount); // number of rows
const updateResult = await db.query(
"UPDATE users SET active = ? WHERE id = ?",
[false, userId]
);
console.log(updateResult.rowCount);Placeholder support:
- Recommended portable style:
? - QueryMesh rewrites
?to the connected dialect ($1,?,@p1, or:p1). - Native placeholder styles are also accepted and normalized:
$1,@p1,:p1.
Notes:
db.query(...)returns a rows array that also exposes.rowsand.rowCount.db.exec(...)no longer executes raw SQL. Usedb.query(...)for raw SQL commands.schema().exec()still exists for schema-builder statements.- MongoDB rejects raw SQL; use the QueryMesh builder or native Mongo collection APIs.
raw(...) vs db.query(...)
Use raw(...) inside a QueryMesh builder. Use db.query(...) when you want to execute a full SQL string directly.
| Need | Use | Example |
|---|---|---|
| SQL expression inside a builder | raw(...) | .where(raw("LOWER(email)"), value) |
| SQL value/function inside mutation data | raw(...) | .update({ updated_at: raw("CURRENT_TIMESTAMP") }) |
| Full SELECT written manually | db.query(...) | db.query("SELECT * FROM users WHERE id = ?", [id]) |
| Full UPDATE/DELETE/DDL written manually | db.query(...) | db.query("UPDATE users SET active = ? WHERE id = ?", [false, id]) |
// raw(...) is a fragment inside the builder
await db
.table("users")
.where(raw("LOWER(email)"), email.toLowerCase())
.get();
// query(...) executes the whole SQL statement
const result = await db.query(
"SELECT * FROM users WHERE LOWER(email) = ?",
[email.toLowerCase()]
);
console.log(result.rows);Grouped WHERE (explicit parentheses)
Use callback groups when you need exact boolean grouping.
// (a = x AND b = y) OR (c = z OR d = xx)
const rows = await db
.table("users")
.where((q) => q.where("a", x).where("b", y))
.orWhere((q) => q.where("c", z).orWhere("d", xx))
.get();Nested groups are supported:
// (a=x AND b=y) OR ((c=z OR d=xx) OR (a1=x1 AND b1=y1))
await db.table("users")
.where((q) => q.where("a", x).where("b", y))
.orWhere((q) =>
q.where((g) => g.where("c", z).orWhere("d", xx))
.orWhere((g) => g.where("a1", x1).where("b1", y1))
)
.get();You can negate grouped logic:
// NOT (a = 1 OR b = 2)
await db.table("users")
.whereNot((q) => q.where("a", 1).orWhere("b", 2))
.get();IS / NULL checks
await db.table("users")
.whereIs("verified", true)
.whereIsNotNull("deleted_at")
.get();Aliases:
whereNull()/whereNotNull()whereIsNull()/whereIsNotNull()
ANY / ALL
// Postgres array example
await db.table("users")
.whereAny("score", ">", [10, 20, 30])
.get();
// Subquery example (SQL dialects)
const limits = db.table("limits").select("max_score");
await db.table("users")
.whereAll("score", "<=", limits)
.get();Notes:
- SQL dialects:
ANY/ALLsupport both subquery/raw sources and literal arrays. - MongoDB:
ANY/ALLsupports literal arrays (not subqueries/raw SQL).
Joins
Equality joins are the common case:
await db
.table("orders")
.leftJoinOn("users", "orders.user_id", "users.id")
.select(["orders.id", "users.email"])
.get();The long form still works:
await db
.table("orders")
.join("left", "users", "orders.user_id", "=", "users.id")
.get();Multiple ON conditions use andOn(...) or orOn(...) after the join:
// SQL shape:
// LEFT JOIN A ON B.X = A.X AND B.Y = A.Y
await db
.table("B")
.leftJoinOn("A", "B.X", "A.X")
.andOn("B.Y", "A.Y")
.get();Use the explicit operator form when needed:
await db
.table("orders")
.leftJoin("shipments", "orders.id", "=", "shipments.order_id")
.andOn("orders.created_at", "<=", "shipments.created_at")
.get();Rules for join ON fields:
- For MongoDB, the first field should be from the current table/collection and the second field should be from the joined table/collection.
- Keep predicates that belong to the join inside
andOn(...); moving them towhere(...)can changeLEFT JOINbehavior.
Aliased joins are supported (useful when joining the same table multiple times):
await db
.table("shops")
.leftJoinOnAs("discounts", "discount", "shops.discountId", "discount.id")
.leftJoinOnAs("discounts", "adminDiscount", "shops.adminDiscountId", "adminDiscount.id")
.select(["shops.id", "discount.name", "adminDiscount.name"])
.get();join...As quick reference:
joinAs(type, table, alias, left, opOrRight, right?)joinOnAs(type, table, alias, left, right)(equality shortcut)innerJoinOn / leftJoinOn / rightJoinOninnerJoinAs / leftJoinAs / rightJoinAsinnerJoinOnAs / leftJoinOnAs / rightJoinOnAsandOn(left, opOrRight, right?) / orOn(left, opOrRight, right?)
Rules:
aliasmust be non-empty.- When you use an alias, reference that alias in selected fields and join conditions (
discount.id, notdiscounts.id). - Use this pattern when the same table/collection is joined more than once.
SQL compile example for aliased joins:
SELECT "shops"."id", "discount"."name", "adminDiscount"."name"
FROM "shops"
LEFT JOIN "discounts" AS "discount"
ON "shops"."discountId" = "discount"."id"
LEFT JOIN "discounts" AS "adminDiscount"
ON "shops"."adminDiscountId" = "adminDiscount"."id"MongoDB uses $lookup:
=joins uselocalField/foreignField.!=,<>,>,>=,<,<=joins use$lookup.pipeline + $expr.- Alias joins map to
$lookup.as, so multiple joins to the same collection can use distinct output keys.
Mongo compile example for aliased joins:
[
{ $lookup: { from: "discounts", localField: "discountId", foreignField: "id", as: "discount" } },
{ $unwind: { path: "$discount", preserveNullAndEmptyArrays: true } },
{ $lookup: { from: "discounts", localField: "adminDiscountId", foreignField: "id", as: "adminDiscount" } },
{ $unwind: { path: "$adminDiscount", preserveNullAndEmptyArrays: true } }
]Output shape example (Mongo):
[
{
"id": 1,
"discount": { "name": "Summer 10%" },
"adminDiscount": { "name": "Staff 20%" }
}
]Group by / Having
const stats = await db
.table("orders")
.select("status")
.count("*", "total")
.min("amount", "min_amount")
.max("amount", "max_amount")
.groupBy("status")
.having("total", ">", 10)
.orderBy("total", "desc")
.get();UNION / UNION ALL
const q1 = db.table("users").select("id").where("kind", "user");
const q2 = db.table("admins").select("id").where("enabled", true);
const q3 = db.table("guests").select("id").where("active", true);
const rows = await q1.union(q2).unionAll(q3).get();Notes:
- SQL dialects compile native
UNION/UNION ALL. - MongoDB supports
UNION/UNION ALLwith QueryBuilder sources.
Insert / Update / Delete
await db.table("users").insert({ email: "[email protected]" }).run();
await db.table("users").update({ name: "A" }).where("id", 1).run();
await db.table("users").delete().where("id", 1).run();INSERT ... SELECT
const src = db.table("users_archive")
.select(["id", "email"])
.where("is_active", true);
await db.table("users")
.insertSelect(["id", "email"], src)
.run();Mongo note:
insertSelecton Mongo requires a QueryBuilder source and explicit source select columns (no raw/string SQL source).
Upsert
Postgres:
await db.table("users")
.insert({ email: "[email protected]", name: "A" })
.onConflictDoUpdate("email", { name: "A" })
.returning(["id"])
.run();SQL Server:
await db.table("users")
.insert({ id: 1, email: "[email protected]", name: "A" })
.onConflictDoUpdate("email", { name: "A2" }) // compiled as MERGE
.returning(["id"]) // native OUTPUT
.run();Oracle:
await db.table("users")
.insert({ id: 1, email: "[email protected]", name: "A" })
.onConflictDoUpdate("email", { name: "A2" }) // compiled as MERGE
.run();MySQL:
await db.table("users")
.insert({ email: "[email protected]", name: "A" })
.onDuplicateKeyUpdate({ name: "A" })
.run();MongoDB:
await db.table("users")
.insert({ email: "[email protected]", name: "A" })
.onConflictDoUpdate("email", { name: "A2" }) // mapped to Mongo upsert
.returning(["email", "name"]) // best-effort returned docs
.run();Notes:
onConflictDoUpdateis native on pg, and mapped throughMERGEon mssql/oracle.- Current mssql/oracle/mongo implementation supports single-row
insert(...)foronConflictDoUpdate(notinsertSelect).
Transactions
await db.transaction(async (trx) => {
await trx.table("accounts").update({ balance: 90 }).where("id", 1).run();
await trx.table("accounts").update({ balance: 110 }).where("id", 2).run();
});Transaction behavior:
- SQL dialects borrow one dedicated connection from the pool for the duration of the transaction.
- MongoDB starts a session when the underlying
MongoClientsupports it. - After commit or rollback, QueryMesh releases the borrowed connection/session back to the pool/client.
MongoDB uses sessions when available.
MongoDB transaction notes:
- QueryMesh needs a MongoClient-backed DB handle with
startSession()(officialmongodbdriver client, or Mongoose connection exposing the underlying client). - Multi-document transactions require:
- replica set (MongoDB 4.0+), or
- sharded cluster (MongoDB 4.2+).
- Standalone MongoDB deployments generally do not support multi-document transactions.
Model layer
import { BaseModel } from "querymesh";
class User extends BaseModel {
static table = "users";
static primaryKey = "id";
}
const UserModel = db.model(User);
const u = await UserModel.find(1);
u.name = "New Name";
await u.save();
// Create (insert) using static create
const created = await UserModel.create({
email: "[email protected]",
name: "New User",
});
// Create (insert) using instance save
const draft = new UserModel({
email: "[email protected]",
name: "Draft User",
});
await draft.save();ModelClass vs ModelClass.query():
- Use model helpers directly on the bound model class:
UserModel.create(...),UserModel.find(id),UserModel.all(),instance.save(). - Use
UserModel.query()when you need QueryBuilder features (joins, select lists, aggregates, offset/limit, custom where groups).
// custom query from model
const rows = await UserModel
.query()
.select(["users.id", "users.email"])
.leftJoinOn("departments", "users.id_departments", "departments.id")
.offset(0)
.limit(50)
.get();Schema builder
Create table
await db.schema().createTable("companies", (t) => {
t.increments("id");
t.string("name", 255).notNull();
t.unique(["name"]);
t.timestamps();
}).exec();Type mapping examples:
t.string(name, 80)becomesVARCHAR(80)(mysql),CHARACTER VARYING(80)(pg),NVARCHAR(80)(mssql),VARCHAR2(80)(oracle)t.json(name)becomesJSONB(pg),JSON(mysql),NVARCHAR(MAX)(mssql),CLOB(oracle)
Foreign keys (named constraints)
await db.schema().createTable("orders", (t) => {
t.increments("id");
t.int("user_id").notNull();
t.constraint("fk_orders_user")
.foreign("user_id")
.references("users", "id")
.onDelete("CASCADE");
}).exec();Shorthand:
t.int("user_id").notNull().references("users.id").onDelete("CASCADE");Alter table
await db.schema().alterTable("users", (t) => {
t.string("display_name", 120).nullable();
t.renameColumn("display_name", "name");
t.dropColumnIfExists("old_field");
t.dropConstraint("fk_users_company");
}).exec();Rename and drop tables
await db.schema().renameTable("users", "app_users").exec();
await db.schema().dropTable("app_users", { ifExists: true, cascade: true }).exec();Maintenance utilities
await db.schema().truncateTable("logs").exec();
await db.schema().analyzeTable("users").exec();
await db.schema().optimizeTable("users").exec();
await db.schema().vacuumTable("users", { analyze: true }).exec();
await db.schema().reindexTable("users").exec();
// mysql only
await db.schema().repairTable("users").exec();If you prefer immediate execution without chaining .exec(), use db.maintenance():
await db.maintenance().vacuumDatabase({ verbose: true });
await db.maintenance().reindexTable("users");Notes:
truncateTable:- SQL dialects compile to
TRUNCATE TABLE - MongoDB deletes all documents in the collection with
deleteMany({})
- SQL dialects compile to
analyzeTable:- pg:
ANALYZE - mysql:
ANALYZE TABLE - mssql:
UPDATE STATISTICS - oracle:
DBMS_STATS.GATHER_TABLE_STATS
- pg:
optimizeTableis best-effort and not identical across dialects:- pg:
VACUUM (ANALYZE)by default, orVACUUMwith{ analyze: false } - mysql:
OPTIMIZE TABLE - mssql:
ALTER INDEX ALL ... REORGANIZEorREBUILDwith{ rebuild: true } - oracle / mongo: not supported
- pg:
vacuumTable/vacuumDatabase:- PostgreSQL only
- useful when you want explicit
VACUUMinstead of the broaderoptimizeTablemapping - cannot run inside a transaction block
reindexTable:- pg:
REINDEX TABLEwith optional{ concurrently: true } - mssql:
ALTER INDEX ALL ... REBUILD
- pg:
repairTable:- mysql only
- maps to
REPAIR TABLE - mostly useful for storage engines that support table repair directly
Views
await db.schema()
.createView("active_users", `
SELECT id, email
FROM users
WHERE is_active = true
`)
.exec();
await db.schema().dropView("active_users", { ifExists: true }).exec();Show tables and databases
const tables = await db.schema().showTables(); // SQL tables / Mongo collections
const dbs = await db.schema().showDatabases();Optional schema filter for SQL:
const pgPublicTables = await db.schema().showTables({ schema: "public" });Describe table/database structure
// current database/connection structure
const dbDesc = await db.schema().getDesc();
// table/collection structure
const usersDesc = await db.schema().getDesc("users");
// equivalent:
// await db.schema().getDesc("table", "users");
// optional filtering/options
const publicUsers = await db.schema().getDesc("table", { name: "users", schema: "public" });
const deepDb = await db.schema().getDesc("database", { deep: true });
const withScript = await db.schema().getDesc("users", { includeCreateSql: true });
const dbScript = await db.schema().getDesc("database", { includeCreateSql: true });
// strict cross-dialect shape (same top-level keys for SQL + Mongo)
const strictDb = await db.schema().getDesc("database", { strict: true });
const strictUsers = await db.schema().getDesc("users", { strict: true });includeCreateSql: true adds a createSql string to the response.
PostgreSQL createSql now includes CREATE TABLE IF NOT EXISTS ... with column definitions and primary key constraints (best-effort from catalog metadata).
Create database (uniform options)
createDatabase(name, opts) uses a single option object across dialects. Unsupported fields are ignored by that dialect.
await db.schema().createDatabase("my_app_db", {
ifNotExists: true,
// common
collation: "utf8mb4_unicode_ci", // mysql, mssql
charset: "utf8mb4", // mysql
// postgres
encoding: "UTF8",
locale: "en_US.UTF-8",
owner: "postgres",
template: "template0",
tablespace: "pg_default",
}).exec();Notes:
- Postgres does not support
CREATE DATABASE IF NOT EXISTSin a single SQL statement. QueryMesh performs a pre-check and skips creation when it already exists. - SQL Server wraps creation with
IF DB_ID(...) IS NULLwhenifNotExistsis true. - Oracle database creation is a DBA operation and is not implemented.
Drop database (uniform options)
await db.schema().dropDatabase("my_app_db", {
ifExists: true,
force: true, // best-effort disconnects (pg: terminate sessions, mssql: SINGLE_USER ROLLBACK)
}).exec();Notes:
- Postgres
forceattempts to terminate other sessions connected to the DB (requires appropriate privileges). If it can’t, theDROP DATABASEmay still fail. - SQL Server
forceswitches the DB toSINGLE_USER WITH ROLLBACK IMMEDIATEbefore dropping. - MySQL ignores
force(dropping disconnects sessions automatically).
PostgreSQL schemas
await db.schema().createSchema("audit").exec();
await db.schema().dropSchema("audit", { ifExists: true, cascade: true }).exec();Triggers
await db.schema().createTrigger({
name: "trg_users_touch",
table: "users",
timing: "BEFORE",
events: ["UPDATE"],
body: "NEW.updated_at = CURRENT_TIMESTAMP;",
}).exec();
await db.schema().dropTrigger("trg_users_touch", { table: "users", ifExists: true }).exec();Notes:
- Supported on
pg,mysql,mssql, andoracle. - Not supported on MongoDB in QueryMesh.
Import/Export (backup)
QueryMesh wraps native CLI tools and provides progress events.
const job = db.backup().export({
file: "dump.sql.gz",
format: "plain",
gzip: true,
useStdout: true,
extraArgs: ["--clean"],
});
job.on("progress", (p) => console.log(p));
await job.done;Import progress notes:
- When the tool can read from stdin (for example
mysql,psql,pg_restorecustom/tar,mongorestore --archive), QueryMesh emits byte-based progress. - For non-stdin restore paths (for example directory restores or engine-native restore commands), QueryMesh emits best-effort progress by parsing
%from tool logs plus start/end milestones.
Export progress notes:
useStdout: trueemits byte progress from streamed stdout.- File/directory exports emit byte progress by watching output size growth.
- Tool-reported
%messages are forwarded vialogevents.
The machine running this must have the relevant CLIs installed:
- PG:
pg_dump,pg_restore,psql - MySQL:
mysqldump,mysql - MongoDB:
mongodump,mongorestore - SQL Server:
sqlpackageand/orsqlcmd - Oracle:
expdp,impdp
Tools / Diagnostics
Use db.tools() for runtime checks and version helpers.
const tools = db.tools();
// CLI checks
tools.isPostgresInstalled();
tools.isMongoInstalled();
tools.getToolingStatus(); // current dialect required tools
// DB health/version
await tools.ping();
await tools.getVersion(); // version of current dialect server
await tools.getDiagnostics(); // ping + serverVersion + cli reportTypeScript
QueryMesh ships TypeScript typings.
import QueryMesh from "querymesh";
const db = await QueryMesh.connect({
dialect: "pg",
config: { connectionString: process.env.DATABASE_URL as string },
});API Reference (by module)
Module: QueryMesh (root)
connect({ dialect, config, features?, importer? })raw(sql, params?)id(name)toObjectId(value, opts?)toObjectIdSync(value, ObjectId)
Module: DB
table(name)query(sql, params?)schema()backup()tools()maintenance()switchDatabase(name, opts?)useDatabase(name, opts?)switchDialect(dialect, config, opts?)useDialect(dialect, config, opts?)transaction(async (trx) => ...)model(ModelClass)quote(name)close()
Module: QueryBuilder
- Selection:
select,distinct,aggregate,count,sum,avg,min,max - Filtering:
where,orWhere,whereGroup,orWhereGroup,whereNot,orWhereNot - Predicates:
whereIn,whereNotIn,whereBetween,whereNotBetween,whereNull,whereNotNull,whereIs,whereIsNot - Quantified:
whereAny,whereAll,orWhereAny,orWhereAll - Join/shape:
join,joinAs,joinOn,joinOnAs,innerJoin,leftJoin,rightJoin,innerJoinOn,leftJoinOn,rightJoinOn,innerJoinAs,leftJoinAs,rightJoinAs,innerJoinOnAs,leftJoinOnAs,rightJoinOnAs,andOn,orOn - Set operations:
union,unionAll,clearUnions - Mutation:
insert,insertSelect,update,delete - Upsert:
onConflictDoUpdate,onDuplicateKeyUpdate - Result controls:
groupBy,having,orderBy,limit,offset,returning - Execution:
compile,run,get,first
Module: SchemaBuilder
- DDL:
createTable,alterTable,dropTable,renameTable - Maintenance:
truncateTable,analyzeTable,optimizeTable,vacuumTable,vacuumDatabase,reindexTable,repairTable - Databases:
createDatabase,dropDatabase,showDatabases - PostgreSQL schema:
createSchema,dropSchema - Views:
createView,dropView - Triggers:
createTrigger,dropTrigger - Introspection:
showTables,showDatabases,getDesc(target?, opts?) getDescopts:schema,deep,strict,includeViews,includeDatabases,sampleSize,includeCreateSql- Execute:
exec
Module: MaintenanceManager
truncateTable(name, opts?)analyzeTable(name, opts?)optimizeTable(name, opts?)vacuumTable(name, opts?)vacuumDatabase(opts?)reindexTable(name, opts?)repairTable(name, opts?)
Module: BaseModel
- Static:
bind,query,find,all,create,where,hydrate - Instance:
save,delete
Module: BackupManager
export(options?)import(options?)- Job events:
start,progress,log,error,done
Module: ToolsManager
- Tool checks:
isPostgresInstalled,isPostgreInstalled,isMySqlInstalled,isMsSqlInstalled,isOracleInstalled,isMongoInstalled - CLI metadata:
isCommandAvailable,getCliVersion,getToolingStatus,getAllToolingStatus,getCurrentDialectCliVersion - Runtime metadata:
ping,getVersion,getDiagnostics
Tests
npm testLicense
MIT
