npm install @ferrow/query-builderA chainable SQL query builder for TypeScript/Node. Builds parameterized
SELECT/INSERT/UPDATE/DELETE statements with nested AND/OR
WHERE conditions, joins, ordering, and limit/offset. Produces plain
{ sql, params } — there is no database driver dependency; hand the
output to pg, mysql2, better-sqlite3, or whatever client you use.
Copy src/index.ts into your project, or build this repo (npm run build)
and depend on the compiled dist/.
import { QueryBuilder } from 'query-builder';
const { sql, params } = new QueryBuilder()
.select(['id', 'name'])
.from('users')
.where({ column: 'active', op: '=', value: true })
.orderBy('name')
.limit(10)
.build();
// sql: SELECT "id", "name" FROM "users" WHERE "active" = $1 ORDER BY "name" ASC LIMIT 10
// params: [true].where({
and: [
{ column: 'active', op: '=', value: true },
{ or: [
{ column: 'total', op: '>', value: 100 },
{ column: 'status', op: 'in', value: ['paid', 'shipped'] },
]},
],
})new QueryBuilder()
.select(['users.id', 'orders.total'])
.from('users')
.join('orders', 'users.id = orders.user_id', 'left') // or 'inner' (default)
.build();new QueryBuilder().insert('users', { name: 'Ada', active: true }).build();
new QueryBuilder().update('users', { active: false }).where({ column: 'id', op: '=', value: 42 }).build();
new QueryBuilder().delete_('sessions').where({ column: 'expires_at', op: '<', value: Date.now() }).build();new QueryBuilder({ paramStyle: 'question' }); // ? instead of $1, $2, ...new QueryBuilder({ paramStyle?: 'dollar' | 'question' })—'dollar'($1, $2, ...) is the default;'question'(?) for MySQL/SQLite-style drivers..select(columns?),.from(table),.join(table, on, type?)—typeis'inner'(default) or'left';onis a raw SQL condition string (e.g.'a.id = b.a_id'), not identifier-escaped..insert(table, row),.update(table, set),.delete_(table)— note the trailing underscore ondelete_(deleteis a reserved word)..where(node)— aWhereCondition({ column, op, value? }, ops:= != > < >= <= in like "is null" "is not null") or a nested{ and: WhereNode[] }/{ or: WhereNode[] }..orderBy(column, direction?),.limit(n),.offset(n)..build(): { sql: string; params: unknown[] }.escapeIdentifier(id)— double-quotes a column/table identifier (supportstable.column), throwing on anything that isn't[A-Za-z_][A-Za-z0-9_]*(dot-separated).
- No database driver, connection, or execution — this only builds SQL strings and parameter arrays.
- No SQL dialect abstraction beyond identifier quoting and placeholder
style — e.g.
LIMIT/OFFSETsyntax is Postgres/MySQL/SQLite-compatible but not validated against a specific dialect's full grammar. join()'sonclause is raw SQL you provide — it is not parsed or escaped, so don't interpolate untrusted input into it.- No subqueries,
GROUP BY/HAVING,UNION, or CTEs.
Sponsored by Ferrow
Part of the ferrow-toolkit collection · Sponsored by Ferrow