A progressive Node.js framework for building efficient and scalable server-side applications.
The building blocks of the first-party SQL stores that Nest's packages ship (PostgresWorkflowStore in @nestjs/workflows/postgres, and more to come, on PostgreSQL and MySQL): executors that run a store's SQL through the client the application already has (node-postgres or mysql2, Drizzle, TypeORM, Prisma, Kysely) and join its transactions, a store's versioned migrations with their SQL and its command line, the readiness a store awaits before its statements, and helpers for writing them.
Applications don't install or import it: a package depends on it, and its /postgres and /mysql subpaths re-export the executors and their types.
For a package that ships a store, as a regular dependency (never a peer, so the kit's patch releases dedupe across an application's packages):
$ npm i --save @nestjs/store-kit@nestjs/store-kit: what every dialect shares: the executor types (SqlExecutor,SqlTransaction,SqlTransactionOptions,SqlIsolationLevel,SqlExecuteResult,SqlDialect),isNotATransactionError()(an executor's refusal of an object that isn't a transaction, by its codeERR_SQL_NOT_A_TRANSACTION), the readers of a column's text (toText/toInt/toBool/toJson) andrunStoreCli(), a package's command.SqlExecutor<'postgres'>andSqlExecutor<'mysql'>are each dialect's executors (what the executors return, and what a store's options take): the root'sSqlExecutoralone is either, and/postgres's and/mysql'sSqlExecutor(with theirStoreOptions) are their dialect's.@nestjs/store-kit/postgres: the executors (fromPg,fromDrizzle,fromTypeOrm,fromPrisma,fromKysely, andisNotATransactionError()),StoreSchema(migrations, their SQL, a store's options and readiness), the statement helpers (SqlParams,columns,toText/toInt/toBool/toJson,quoteSchema,advisoryLock) andassertReadCommittedTransaction().@nestjs/store-kit/mysql: the same for MySQL 8.4 LTS and 9.x: the executors (fromMysql2,fromDrizzle,fromTypeOrm,fromPrisma,fromKysely, andisNotATransactionError()),StoreSchema(a store's tables in the connection's database as<schema>_<table>, migrations applied statement by statement underGET_LOCK()and resumed where a failed run stopped), and the statement helpers (SqlParams,columns,toText/toInt/toBool/toJson,quoteIdentifier,quoteTable,keyColumn,lockKeys,ensureLockRows,lockRowId,retryOnDeadlock,mysqlErrorCode).@nestjs/store-kit/testing:sqlExecutorContract(), for an executor of another client.
export const outboxSchema = new StoreSchema({
packageName: '@nestjs/outbox',
storeName: 'PostgresOutboxStore',
command: 'nest-outbox',
defaultSchema: 'nest_outbox',
migrations: [initialMigration], // { version: 1, name: 'initial', up: (s) => [`CREATE TABLE ${s}.messages (...)`] }
createError: (message, details) => new OutboxSchemaError(message, details),
});
export class PostgresOutboxStore implements OutboxStore, OnModuleInit {
static migrationSql(options?: MigrationSqlOptions): string {
return outboxSchema.sql(options);
}
static readonly schemaVersion = outboxSchema.latest;
private readonly executor: SqlExecutor;
private readonly readiness: StoreReadiness;
constructor(options: PostgresOutboxStoreOptions, storage?: OutboxStorage) {
const resolved = outboxSchema.resolveOptions(options); // { executor, schema, migrate }, checked
this.executor = resolved.executor;
this.readiness = outboxSchema.readiness({ ...resolved, logger: new Logger('OutboxModule') });
storage?.registerSource({ messages: this, inbox: this });
}
onModuleInit(): Promise<void> {
return this.readiness.ready(); // migrates (or checks) the schema before the workers start
}
migrate(): Promise<number[]> {
return this.readiness.migrate();
}
}The package's bin (nest-outbox migrate|status|sql):
#!/usr/bin/env node
import { runStoreCli } from '@nestjs/store-kit';
import { outboxSchema } from './outbox.schema.js';
process.exitCode = await runStoreCli([outboxSchema], process.argv.slice(2));npm run test:e2e runs the suite on PGlite, and on PostgreSQL through every executor: SQL_TEST_PG_URL (docker compose up -d starts one on port 55432), else a throwaway cluster from local PostgreSQL binaries, else those tests are skipped with the reason. The MySQL tests (a vitest project of their own, two files at a time, after the PostgreSQL ones) run on SQL_TEST_MYSQL_URL (mysql://root:<password>@127.0.0.1:3306), else they are skipped with the reason. Test databases are named skit_<host>_... on both servers and swept when their process is gone.
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please read more here.
- Author - Kamil Myśliwiec
- Website - https://nestjs.com
- Twitter - @nestframework
Nest is MIT licensed.