Skip to content

Repository files navigation

SQB Logo


NPM Version NPM Downloads CI Tests Test Coverage

About

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.

Why SQB

  • 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.

Getting started

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.

Object-Relational Mapping (ORM)

@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.

Defining entities

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;
}
  • @Column maps a property to a table column: fieldName when it differs from the property name, dataType, notNull, default, autoGenerated: 'increment' | 'uuid' | 'timestamp', and more.
  • @PrimaryKey marks the primary key — decorate more than one property for a composite key.
  • @Link declares a relation to another entity: .toOne(Target, { sourceKey, targetKey }) for a one-to-one/many-to-one, .toMany(...) for a one-to-many (sourceKey/targetKey can be omitted when they're resolvable from a matching @ForeignKey). A related entity is only fetched when a query actually asks for it (via projection), so adding a @Link never 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. an Address), @Index, @Parse/@Serialize (custom value transforms between the database and your class), and lifecycle hooks (@BeforeInsert, @AfterUpdate, etc.).

Using a repository

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.

Transactions

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.

Packages

SQB is a monorepo of small, focused packages. Most projects only need @sqb/builder, @sqb/connect, and one database adapter.

Core

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()).

Database adapters

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 Compatibility

  • node >= 20.x

Thanks to all of the great contributions to the project.

Issues

You can report bugs and discuss features on the GitHub issues page

License

SQB is available under MIT license.

About

Extensible, multi-dialect SQL query builder and Database connection framework for NodeJS

Topics

Resources

Stars

17 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages