Skip to main content

NestJS Integration

@sqb/nestjs wires a SqbClient into a NestJS application as an injectable provider, with the connection lifecycle (connect on bootstrap, close on shutdown) managed for you.

npm install @sqb/nestjs @sqb/connect @sqb/builder

@sqb/nestjs doesn't ship a database driver itself — install and import a database adapter (e.g. @sqb/postgres) the same way you would with a plain @sqb/connect client.

Registering the module

SqbModule.forRoot()

Pass connection options directly via useValue:

import '@sqb/postgres';
import { Module } from '@nestjs/common';
import { SqbModule } from '@sqb/nestjs';

@Module({
imports: [
SqbModule.forRoot({
useValue: {
dialect: 'postgres',
host: 'localhost',
database: 'mydb',
user: 'myuser',
password: 'mypassword',
},
}),
],
})
export class AppModule {}

Under the hood this delegates to SqbCoreModule, which provides an SqbClient instance built from the given SqbClientConnectionOptions and exposes it for injection (see Injecting the client below).

SqbModule.forRootAsync()

Use forRootAsync() when the connection options need to be built asynchronously — for example, read from a NestJS ConfigService:

import '@sqb/postgres';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SqbModule } from '@sqb/nestjs';

@Module({
imports: [
SqbModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
dialect: 'postgres',
host: config.get('DB_HOST'),
database: config.get('DB_NAME'),
user: config.get('DB_USER'),
password: config.get('DB_PASSWORD'),
}),
}),
],
})
export class AppModule {}

useFactory may return its result synchronously or as a Promise — either form is accepted.

Injecting the client

By default, the provider token is the SqbClient class itself, so a consumer can inject it by type without an explicit @Inject():

import { Injectable } from '@nestjs/common';
import { SqbClient } from '@sqb/connect';

@Injectable()
export class UsersService {
constructor(private readonly client: SqbClient) {}

findAll() {
return this.client.execute('select * from users');
}
}

Multiple connections

To register more than one connection in the same application (or to avoid depending on the SqbClient class as a token), pass a custom token and inject it explicitly with @Inject():

SqbModule.forRoot({
token: 'READ_REPLICA',
useValue: { dialect: 'postgres', host: 'replica.internal', database: 'mydb' },
});
import { Inject, Injectable } from '@nestjs/common';
import { SqbClient } from '@sqb/connect';

@Injectable()
export class ReportsService {
constructor(
@Inject('READ_REPLICA')
private readonly client: SqbClient,
) {}
}

Module options

global

Set global: true to register the module as a global NestJS module, so the SqbClient provider is available application-wide without re-importing SqbModule (or the module that imports it) into every feature module:

SqbModule.forRoot({
global: true,
useValue: { dialect: 'postgres', database: 'mydb' },
});

lazyConnect

By default the module calls client.test() during onApplicationBootstrap to verify connectivity and log the result. Set lazyConnect: true to skip this and connect lazily on first use instead:

SqbModule.forRoot({
useValue: {
dialect: 'postgres',
database: 'mydb',
lazyConnect: true,
},
});

shutdownWaitMs

Number of milliseconds to wait for in-flight operations when closing the connection pool during onApplicationShutdown (passed straight through to client.close()). Defaults to 10:

SqbModule.forRoot({
useValue: {
dialect: 'postgres',
database: 'mydb',
shutdownWaitMs: 5000,
},
});

logger

Pass a NestJS LoggerService instance (or a string logger context name) to control where connection lifecycle messages (Waiting to connect to Database..., Database connection established, connection failures) are logged.

envPrefix

Any connection field left unset in useValue/useFactory is filled in from environment variables, using envPrefix (default 'SQB_') as the variable prefix:

FieldEnvironment variable
dialect${envPrefix}DIALECT
name${envPrefix}CONNECTION_NAME
host${envPrefix}HOST
port${envPrefix}PORT
database${envPrefix}DATABASE
schema${envPrefix}SCHEMA
user${envPrefix}USER
password${envPrefix}PASSWORD
driver${envPrefix}DRIVER
pool.max${envPrefix}POOL_MAX
pool.min${envPrefix}POOL_MIN
pool.idleTimeoutMillis${envPrefix}POOL_IDLE_TIMEOUT
pool.acquireTimeoutMillis${envPrefix}POOL_ACQUIRE_TIMEOUT
pool.acquireMaxRetries${envPrefix}POOL_ACQUIRE_MAX_RETRIES
pool.acquireRetryWait${envPrefix}POOL_ACQUIRE_RETRY_WAIT
pool.fifo${envPrefix}POOL_FIFO
pool.maxQueue${envPrefix}POOL_MAX_QUEUE
pool.minIdle${envPrefix}POOL_MIN_IDLE
pool.validation${envPrefix}POOL_VALIDATION
pool.houseKeepInterval${envPrefix}POOL_HOUSE_KEEP_INTERVAL

Values already present in useValue/the object returned by useFactory always win — the environment is only consulted for fields left unset. This lets you keep secrets like the password out of source and supply everything through the environment instead:

// .env
// SQB_DIALECT=postgres
// SQB_HOST=localhost
// SQB_DATABASE=mydb
// SQB_USER=myuser
// SQB_PASSWORD=mypassword

SqbModule.forRoot(); // options come entirely from SQB_* environment variables

Pass a custom prefix if SQB_ collides with something else in your environment:

SqbModule.forRoot({ envPrefix: 'MYAPP_DB_' });

See also