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​