Nestjs Drizzle
Reference

Config types

SqlCrudConfig and DrizzleCrudConfig: the two interfaces that drive the module.

The package exposes two config types: DrizzleCrudConfig (the input to forRoot / forRootAsync) and SqlCrudConfig (the merged, per-entity config the base class actually uses).

DrizzleCrudConfig

The input to DrizzleCrudModule.forRoot(config) and forRootAsync. It describes the project-level configuration: the database connection, schema, and global defaults.

interface DrizzleCrudConfig {
  /** Required. */
  : 'postgresql' | 'mysql';

  /** PostgreSQL: connection string. The module builds the connection. */
  ?: string;

  /** Any dialect: pre-built Drizzle instance. Required for MySQL. */
  ?: any;

  /** Required when building from connectionString. */
  ?: <string, unknown>;

  /** Per-project defaults applied to every entity unless overridden. */
  ?: {
    ?: boolean;
    ?: boolean;
    /** Either key alone keeps the other's default. */
    ?: { ?: number; ?: number };
    ?: 'asc' | 'desc';
  };

  /** Dialect tuning. */
  ?: {
    ?: boolean;
    ?: boolean;
    /** @deprecated Has no effect. */
    ?: boolean;
    /** @deprecated Has no effect. */
    ?: boolean;
  };

  /** Module-level write events. See /docs/events. */
  ?: {
    ?: boolean;   // default false
    ?: <<string, unknown>>; // CrudSubscriber[]
  };
}

Defaults

defaults: {
  softDelete: true,
  timestamps: true,
  pagination: { defaultLimit: 20, maxLimit: 100 },
  // sortOrder: opt-in (no fallback)
}

sql: {
  caseSensitive: false,
  useReturning: dialect === 'postgresql',
}

sql.jsonSupport, sql.enableFullTextSearch and primaryKeyType are deprecated. Nothing reads them, so they have no effect. They are still accepted so existing configs compile, and will be removed in the next major version. hooks.enableGlobalHooks was deprecated in 3.3.1 and does something again since 3.5.0: it turns on write events.

SqlCrudConfig

The fully-resolved, per-entity configuration. The package constructs this by layering:

global defaults (DrizzleCrudConfig)
  → @CrudService decorator metadata
    → forFeature { config }
      → db, table (set by forFeature)

softDelete, timestamps, pagination and sql merge one level deep at every layer, so each layer only names the settings it changes. Any other key replaces the value from the layer before. When the service is built it checks that the softDelete.column and timestamps keys exist on the table, and throws with the key to use if one does not.

interface SqlCrudConfig {
  // ---- Set by the module ----
  : 'postgresql' | 'mysql';
  : any;
  : any;

  // ---- Primary key ----
  : string;              // default 'id'
  /** @deprecated Has no effect: the type comes from the column. */
  ?:
    | 'serial' | 'bigserial' | 'int' | 'bigint' | 'uuid';

  // ---- Soft delete ----
  ?: {
    : boolean;
    : string;               // the table key, e.g. 'deleted_at' or 'deletedAt'
  };

  // ---- Timestamps ----
  ?: {
    : string;            // the table key, e.g. 'created_at'
    : string;            // the table key, e.g. 'updated_at'
  };

  // ---- Pagination ----
  ?: {
    : number;
    : number;
  };

  // ---- Default ORDER BY ----
  ?: <{
    : string;
    : 'asc' | 'desc';
  }>;

  // ---- Dialect tuning ----
  ?: {
    : boolean;
    : boolean;
    /** @deprecated Has no effect. */
    : boolean;
    /** @deprecated Has no effect. */
    : boolean;
  };

  // ---- Relations: belongs-to, has-many, many-to-many, nested ----
  ?: <string, >;

  // ---- Allowlists (CrudAllowlists). Unset allows everything. ----
  ?: string[];   // columns and 'relation.column'
  ?: string[];     // columns and 'belongsTo.column'
  ?: string[];   // columns and dotted paths
  ?: string[];
  ?: string[];   // relation paths, also for counts
  ?: string[];     // create, update, upsert
  ?: string[];   // nested create, attach, detach, sync

  // ---- Tenant scope: ANDed into every read and write, set on create ----
  ?:
    | <string, unknown>
    | ((: <string, unknown>) => <string, unknown> | null);

  // ---- Optimistic locking: integer column, +1 on every write ----
  ?: string;

  // ---- Event bus, set by the module when hooks.enableGlobalHooks is on ----
  ?: { (: unknown): <void> }; // a CrudEventBus
}

The allowlists are described in Allowlists. Arrays replace, they do not merge, across the config layers. scope is on Multi-tenancy, versionColumn on Optimistic locking and events on Global hooks and write events. A versionColumn or a fixed scope key that is not a column fails at startup.

RelationConfig, RelationsConfig and RelationType

type  = 'belongsTo' | 'hasMany' | 'manyToMany';

interface RelationConfig {
  : any;                  // the target Drizzle table
  ?: ;         // defaults to 'belongsTo'
  // belongs-to
  ?: string;           // the FK column on the *current* table (required)
  ?: string;         // the target column (defaults to 'id')
  // has-many
  ?: string;         // the column on the *target* table pointing back here (required)
  ?: string;          // the column on the current table it matches (defaults to that table's primary key)
  // many-to-many
  ?: any;               // the join table (required)
  ?: string;    // join column matching parentKey (required)
  ?: string;  // join column matching references (required)
  // collections only
  ?: [];      // how to order the children, applied in SQL
  // relations of the RELATED table, addressable as 'relName.childName'
  ?: <string, RelationConfig>;
}

type  = <string, RelationConfig>;

A relation missing its required keys throws when the service is constructed, nested relations included, with the full path in the message. See Relations.

RelationNode and SelectTree

The resolved eager-loading plan and the parsed options.select. Both are exported for subclasses that override the loader; normal use never touches them.

interface RelationNode {
  : string;
  : ;
  : RelationNode[];
  ?: boolean;   // folded into the main query as a LEFT JOIN
}

interface SelectTree {
  : string[];
  : <string, SelectTree>;
}

SqlSearchOptions

SqlOperationOptions.search is accepted by findAll and count.

type  = 'ilike' | 'fullText';

interface SqlSearchOptions {
  : string;
  : string[];
  ?: ;
}

The default mode is ilike, which searches each configured column with a contains-style pattern and joins the column predicates with OR. fullText is PostgreSQL-only and compiles to to_tsvector(...) @@ plainto_tsquery(...).

Filter, sort, cursor and upsert types

Added in 3.4.0. See Filtering, Pagination & sorting, Cursor pagination and Upsert.

type FilterScalar = string | number | boolean | bigint | Date;
type ColumnFilter = FilterScalar | FilterScalar[] | FilterOperators | null | undefined;

interface FilterOperators {
  eq?; neq?; gt?; gte?; lt?; lte?;
  like?: string; ilike?: string;
  in?: unknown[]; notIn?: unknown[]; between?: [unknown, unknown];
  isNull?: boolean | 'true' | 'false'; isNotNull?: boolean | 'true' | 'false';
  startsWith?: string; endsWith?: string; contains?: string;
  iStartsWith?: string; iEndsWith?: string; iContains?: string;
  arrayContains?: unknown[]; arrayOverlaps?: unknown[]; jsonContains?: unknown;
}
type FilterOperator = keyof FilterOperators;

interface FilterGroup { and?: CrudFilter[]; or?: CrudFilter[]; not?: CrudFilter }
type CrudFilter = FilterGroup & { [key: string]: ColumnFilter | CrudFilter | CrudFilter[] };

type SortInput = string | Array<string | SortColumn>;

interface CursorPaginationOptions {
  cursor?: string | null;
  after?: string | null;   // alias of cursor
  limit?: number;
  sortBy?: SortInput;
  sortOrder?: 'asc' | 'desc';
  count?: boolean;         // default false
}
interface CursorPage<T> { data: T[]; limit: number; nextCursor: string | null; total?: number }

interface UpsertOptions { conflictTarget?: string | string[]; update?: string[] }
type BulkMode = 'set' | 'perItem';

3.5.0 types

See Relation filters, Aggregates, Pagination, Multi-tenancy, Global hooks and write events and CRUD controller factory.

interface CollectionFilter { some?: CrudFilter; none?: CrudFilter; every?: CrudFilter }
type CollectionQuantifier = keyof CollectionFilter;

interface CrudPage<T, Total extends number | null = number> {
  data: T[]; total: Total; page: number; limit: number; hasMore?: boolean;
}
interface UncountedCrudPage<T> extends CrudPage<T, null> { hasMore: boolean }

type RelationCounts = string[] | Record<string, true | CrudFilter>;
interface CountByRow<V = unknown> { value: V; count: number }

type CrudContextValue = Record<string, unknown>;
type CrudScopeValues = Record<string, unknown>;
type CrudScope = CrudScopeValues | ((context: CrudContextValue) => CrudScopeValues | null);

type CrudWriteOperation = 'create' | 'update' | 'upsert' | 'softDelete' | 'restore'
  | 'delete' | 'attach' | 'detach' | 'sync';
type CrudEventPhase = 'before' | 'after';
interface CrudEvent<T = any> {
  entity: string; operation: CrudWriteOperation; phase: CrudEventPhase; bulk: boolean;
  ids: unknown[]; input?: unknown; rows: T[]; transaction?: unknown; context?: CrudContextValue;
}
interface CrudSubscriber<T = any> {
  entities?: string[];
  beforeWrite?(e: CrudEvent<T>): void | Promise<void>;
  afterWrite?(e: CrudEvent<T>): void | Promise<void>;
  // plus before/after for Create, Update, Upsert, SoftDelete, Restore,
  // Delete, Attach, Detach and Sync: beforeCreate, afterCreate, ...
}

type CrudRouteName = 'list' | 'trash' | 'get' | 'create' | 'update' | 'delete'
  | 'softDelete' | 'restore' | 'bulkCreate' | 'bulkUpdate' | 'bulkDelete' | 'bulkRestore';
interface CrudRouteOptions { enabled?: boolean; decorators?: MethodDecorator[] }
interface CrudControllerOptions {
  path?: string;
  idType?: 'int' | 'uuid' | 'string';
  dto?: { create?: Type<unknown>; update?: Type<unknown> };
  query?: ParseCrudQueryOptions;
  routes?: Partial<Record<CrudRouteName, boolean | CrudRouteOptions>>;
  decorators?: MethodDecorator[];
  classDecorators?: ClassDecorator[];
  deleteMode?: 'hard' | 'soft';
  openapi?: true | ApiCrudSource;
}

type ApiCrudSource = (ParseCrudQueryOptions & { table: any }) | (abstract new (...args: any[]) => unknown);
interface ApiCrudQueryOptions { cursor?: boolean; trash?: boolean }

PaginationOptions gains withCount?: boolean, and ParsedCrudQuery gains pagination.withCount and counts.

ParseCrudQueryOptions and ParsedCrudQuery are on Parse query strings into filters.

CrudFeature

The input to forFeature:

type  = <
  <SqlCrudConfig, 'softDelete' | 'timestamps' | 'pagination' | 'sql'>
> & {
  ?: <<SqlCrudConfig['softDelete']>>;
  ?: <<SqlCrudConfig['timestamps']>>;
  ?: <<SqlCrudConfig['pagination']>>;
  ?: <<SqlCrudConfig['sql']>>;
};

interface CrudFeature {
  : typeof ;
  : any;                  // Drizzle table
  ?: ;  // per-entity overrides
}

CrudFeatureConfig is exported. Every key is optional, db, table and dialect are set for you, and the four object settings merge with the project defaults. Before 3.3.1 config was typed Record<string, any>, so a typo or a half-filled pagination compiled.

SqlDialect, PrimaryKeyType, SortColumn, SortOrder

type  = 'postgresql' | 'mysql';
type  = 'serial' | 'bigserial' | 'int' | 'bigint' | 'uuid';
type  = 'asc' | 'desc';
interface SortColumn { : string; :  }

Next

On this page