Nestjs Drizzle
Configuration

forRoot

Configure the database connection once in AppModule via DrizzleCrudModule.forRoot.

DrizzleCrudModule.forRoot(config) is called once in your AppModule. It owns the database connection, exposes it globally, and closes it on application shutdown (when the module built it from a connectionString).

Signature

DrizzleCrudModule.forRoot(config: DrizzleCrudConfig): DynamicModule

Options

FieldTypeDescription
dialect'postgresql' | 'mysql'Required. Database dialect.
connectionStringstringConnection string. The module builds the connection (PostgreSQL only).
dbDrizzle instanceAlternatively, pass a Drizzle instance you built yourself (any dialect).
schemaRecord<string, unknown>Drizzle schema, used when building from connectionString.
defaults.softDeletebooleanEnable soft delete for all entities (default true).
defaults.timestampsbooleanAuto-manage created_at / updated_at for all entities (default true).
defaults.pagination{ defaultLimit?: number; maxLimit?: number }Pagination defaults (default { 20, 100 }). Set one key and the other keeps its default.
defaults.sortOrder'asc' | 'desc'Opt-in default sort. Entities without an explicit defaultSort fall back to ordering by their created_at column in this direction. Omit it and findAll orders by the primary key only.
sql{ caseSensitive?, useReturning? }Dialect tuning. useReturning defaults to true for PostgreSQL, false for MySQL. Every key is optional.

Provide exactly one of connectionString or db. The module will throw if you pass neither, or both.

If your tables have no created_at / updated_at / deleted_at columns, set defaults: { softDelete: false, timestamps: false }. If your schema uses camelCase keys (deletedAt: timestamp('deleted_at')), set the key names per entity in forFeature. Since 3.3.1 a service whose configured soft-delete or timestamp key is not on its table fails at startup, and the message names the key to use.

PostgreSQL: let the module build the connection

app.module.ts
import { Module } from '@nestjs/common';
import { DrizzleCrudModule } from 'nestjs-drizzle-crud';
import { schema } from './db/schema';

@Module({
  imports: [
    DrizzleCrudModule.forRoot({
      dialect: 'postgresql',
      connectionString: process.env.DATABASE_URL,
      schema,
      defaults: {
        softDelete: true,
        timestamps: true,
        pagination: { defaultLimit: 20, maxLimit: 100 },
      },
      sql: {
        caseSensitive: false,
      },
    }),
  ],
})
export class AppModule {}

The module imports the postgres driver on your behalf and builds a Drizzle instance from your schema. The connection is closed automatically on app shutdown.

If you want full control over the connection (pool size, SSL, advanced mysql2 options), or you are on MySQL, pre-build the Drizzle instance and pass it as db:

app.module.ts (Postgres)
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { DrizzleCrudModule } from 'nestjs-drizzle-crud';
import { schema } from './db/schema';

const client = postgres(process.env.DATABASE_URL!);
const db = drizzle(client, { schema });

@Module({
  imports: [
    DrizzleCrudModule.forRoot({
      dialect: 'postgresql',
      db,
    }),
  ],
})
export class AppModule {}
app.module.ts (MySQL)
import { drizzle } from 'drizzle-orm/mysql2';
import mysql from 'mysql2/promise';
import { DrizzleCrudModule } from 'nestjs-drizzle-crud';
import { schema } from './db/schema';

const pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  connectionLimit: 10,
});
const db = drizzle(pool, { schema });

@Module({
  imports: [
    DrizzleCrudModule.forRoot({
      dialect: 'mysql',
      db,
    }),
  ],
})
export class AppModule {}

When you pass db, the module does not close the connection on shutdown. Closing it is your responsibility, and that makes it easy to share the same pool across other Drizzle consumers (custom services, raw queries, migrations).

sql tuning

OptionDefaultWhat it does
caseSensitivefalseWhen false, bare string filter values are compiled to lower(column) = lower(value) (case-insensitive exact match). When true, bare strings use plain equality (collation-dependent). Use the explicit { like } / { ilike } operators for pattern matching.
useReturningtrue (Postgres) / false (MySQL)Use RETURNING after INSERT to fetch the created row. MySQL does not support RETURNING, so leave it off.
jsonSupporttrueDeprecated, no effect. Nothing reads it. It is still accepted so existing configs compile, and will be removed in the next major version.
enableFullTextSearchfalseDeprecated, no effect. fullTextSearch() and search.mode: 'fullText' work on PostgreSQL whatever this is set to. Removed in the next major version.

hooks.enableGlobalHooks is deprecated too: there are no global hooks, so it does nothing. Override the hooks on each service instead.

caseSensitive: false only applies lower() to text columns (text, varchar, char). A string filter on an integer, uuid or enum column is a plain =, so a query string value such as ?author_id=1 works.

Defaults cheat sheet

defaults: {
  softDelete: true,                      // deletes set deleted_at = now()
  timestamps: true,                      // create/update stamp created_at/updated_at
  pagination: { defaultLimit: 20, maxLimit: 100 },
  sortOrder: 'desc',                     // opt-in: created_at fallback for findAll
},
sql: {
  caseSensitive: false,
  useReturning: dialect === 'postgresql',
}

When to use forRootAsync

If your config comes from ConfigService (env validation, secrets, dynamic config), use forRootAsync instead of forRoot.

Next

On this page