SQB is a modern, all-in-one database framework for Node.js. Instead of picking a query builder, a connection pool, an ORM and a migration tool separately, SQB gives you all of them as one coherent, TypeScript-first toolkit — and the same code works against PostgreSQL, MySQL, MariaDB, Microsoft SQL Server, Oracle Database and SQLite, because every piece is built around a shared, dialect-agnostic query model.
You write queries once, as plain JS/TS objects:
import { Select, Eq } from '@sqb/builder';
const query = Select('id', 'given_name', 'family_name')
.from('customers')
.where(Eq('active', true))
.orderBy('id')
.limit(10);...and SQB turns that into the correct SQL for whichever database you connect to — LIMIT 10
for PostgreSQL/MySQL/MariaDB/SQLite, FETCH FIRST 10 ROWS ONLY for Oracle, OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY for SQL Server — without you having to think about the difference.
- One codebase, many databases. Swap the database adapter and your query code keeps working — useful for libraries, multi-tenant apps, or simply not locking yourself into one vendor.
- Layered, not monolithic. Use just the query builder if that's all you need, add the connection/ORM layer when you need pooling and repositories, bring in the migrator or the NestJS module only if your project needs them.
- Fast and full-featured. Built for low memory overhead and efficient handling of large result sets (streaming cursors, not just "load everything into an array") — without cutting corners on capability.
- Modern TypeScript. Full type inference for query builders and entities, targeting current Node.js and JavaScript standards.
Every project needs the query builder plus exactly one database adapter. Install both:
$ npm install @sqb/builder @sqb/connect @sqb/postgres --save(swap @sqb/postgres for whichever database you're using — see the package list
below)
import '@sqb/postgres';
import { SqbClient } from '@sqb/connect';
import { Select, Eq } from '@sqb/builder';
const client = new SqbClient({
dialect: 'postgres',
host: 'localhost',
database: 'mydb',
});
const query = Select('id', 'given_name').from('customers').where(Eq('active', true));
const result = await client.execute(query);
console.log(result.rows);Prefer working with typed entities and repositories instead of raw queries? See the next section.
Visit sqbjs.org for the full documentation.
@sqb/connect includes a full-featured, decorator-based ORM on top of the query builder and
connection layer described above. Model your tables as classes once, then read and write them
through a typed Repository instead of hand-writing SQL for everyday CRUD — while still being
able to drop down to raw queries whenever you need to.
import { BaseEntity, Column, Entity, Link, PrimaryKey } from '@sqb/connect';
@Entity('countries')
export class Country extends BaseEntity {
@PrimaryKey()
@Column()
declare code: string;
@Column()
declare name: string;
}
@Entity('customers')
export class Customer extends BaseEntity {
@PrimaryKey()
@Column({ autoGenerated: 'increment' })
declare id?: number;
@Column({ fieldName: 'given_name' })
declare givenName: string;
@Column({ fieldName: 'country_code' })
declare countryCode: string;
@Column({ default: true })
declare active: boolean;
@(Link().toOne(Country, { sourceKey: 'countryCode', targetKey: 'code' }))
declare readonly country?: Country;
}@Columnmaps a property to a table column:fieldNamewhen it differs from the property name,dataType,notNull,default,autoGenerated: 'increment' | 'uuid' | 'timestamp', and more.@PrimaryKeymarks the primary key — decorate more than one property for a composite key.@Linkdeclares a relation to another entity:.toOne(Target, { sourceKey, targetKey })for a one-to-one/many-to-one,.toMany(...)for a one-to-many (sourceKey/targetKeycan be omitted when they're resolvable from a matching@ForeignKey). A related entity is only fetched when a query actually asks for it (viaprojection), so adding a@Linknever adds an implicit join to every query that touches the entity.- Entities also support
@Embedded(map a group of columns to a nested object, e.g. anAddress),@Index,@Parse/@Serialize(custom value transforms between the database and your class), and lifecycle hooks (@BeforeInsert,@AfterUpdate, etc.).
const repo = client.getRepository(Customer);
// Find many rows, with a filter and an eager-loaded relation
const customers = await repo.findMany({
filter: { active: true },
projection: ['id', 'givenName', 'country'],
sort: ['givenName'],
limit: 20,
});
// Find a single row by primary key
const customer = await repo.findById(1);
// Create
const created = await repo.create({ givenName: 'Jane', countryCode: 'US' });
// Update
await repo.update(1, { givenName: 'Janet' });
// Delete
await repo.delete(1);Repository also has findOne, count, exists, and bulk updateMany/deleteMany
variants that act on a filter instead of a single key (a non-empty filter is required for
those, precisely so a typo can't silently wipe an entire table). Filters accept either a plain
object ({ city: 'Istanbul' }, { 'age >=': 18 }) or @sqb/builder operators (Eq, In,
And, Or, ...) for anything more complex.
await client.acquire(async connection => {
const repo = connection.getRepository(Customer);
await connection.startTransaction();
try {
await repo.update(1, { active: false });
await repo.create({ givenName: 'New Customer', countryCode: 'US' });
await connection.commit();
} catch (e) {
await connection.rollback();
throw e;
}
});See the @sqb/connect package for the full ORM API.
SQB is a monorepo of small, focused packages. Most projects only need @sqb/builder,
@sqb/connect, and one database adapter.
| Package | Description |
|---|---|
@sqb/builder |
The SQL query builder at the core of everything else. Compose Select/Insert/Update/Delete statements as JS objects and serialize them to any supported dialect's SQL text — no driver or network connection required. |
@sqb/connect |
The connection layer: pooling, transactions, cursors/streaming, and a full-featured ORM (Repository, @Entity/@Column/@Link decorators) built on top of @sqb/builder. |
@sqb/migrator |
A versioned schema and data migration runner, with SQL-script, data-insert and custom-function migration tasks. |
@sqb/nestjs |
A NestJS module that registers an @sqb/connect client as an injectable, application-scoped provider (SqbModule.forRoot()/forRootAsync()). |
Install the one adapter matching your database — each pulls in its own SQL dialect package automatically, so there's nothing else to add.
| Database | Package | Driver |
|---|---|---|
| PostgreSQL | @sqb/postgres |
postgrejs (pure JS) |
| MySQL | @sqb/mysql |
mysql2 |
| MariaDB | @sqb/mariadb |
mariadb (official) |
| Microsoft SQL Server | @sqb/mssql |
mssql (pure JS, tedious-based) |
| Oracle Database | @sqb/oracle |
oracledb |
| SQLite (native) | @sqb/sqlite |
node:sqlite / bun:sqlite |
| SQLite (WebAssembly — browser, etc.) | @sqb/sqljs |
sql.js |
Each adapter has a matching *-dialect package (e.g. @sqb/postgres-dialect)
that teaches @sqb/builder that database's SQL syntax and quirks — LIMIT/OFFSET vs.
FETCH/OFFSET, RETURNING support, reserved words, and so on. It's loaded automatically
when you import the adapter package, so you only need to depend on a -dialect package
directly if you want to generate SQL text for a database you're not actually connecting to.
- node >= 20.x
Thanks to all of the great contributions to the project.
You can report bugs and discuss features on the GitHub issues page
SQB is available under MIT license.
