npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nestjs-drizzle-pg

v4.0.0

Published

Drizzle ORM and PostgreSQL integration for NestJS

Readme

nestjs-drizzle-pg

npm version npm monthly downloads CI

Drizzle ORM and PostgreSQL for NestJS. Register a connection, inject a typed database, and query with Drizzle. The module manages connection creation and shutdown, including independent named databases.

Quick start · Async configuration · Multiple databases · Connection lifecycle · API reference

Install

pnpm add nestjs-drizzle-pg drizzle-orm pg

Requires Node.js 24+ and NestJS 12. Ships ESM, CommonJS, and TypeScript declarations. The package name stays nestjs-drizzle-pg.

Quick start

Set DATABASE_URL in your application environment, then define a schema:

// schema.ts
import { integer, pgTable, text } from "drizzle-orm/pg-core";

export const users = pgTable("users", {
  id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
  name: text("name").notNull(),
});

Register the schema and a PostgreSQL pool in the same module as your service:

// users.module.ts
import { Injectable, Module } from "@nestjs/common";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import { DrizzlePgModule, InjectDrizzlePg } from "nestjs-drizzle-pg";
import * as schema from "./schema";

const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
  throw new Error("DATABASE_URL is required");
}

@Injectable()
export class UsersService {
  constructor(
    @InjectDrizzlePg()
    private readonly db: NodePgDatabase<typeof schema>,
  ) {}

  list() {
    return this.db.query.users.findMany({ limit: 25 });
  }

  create(name: string) {
    return this.db.insert(schema.users).values({ name }).returning();
  }
}

@Module({
  imports: [
    DrizzlePgModule.register({
      pgConfig: { type: "pool", config: { connectionString, max: 10 } },
      drizzleConfig: { schema },
    }),
  ],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

Import UsersModule into your application and inject UsersService where needed. list() returns typed user rows; create() inserts a row and returns the inserted values.

Create the database tables before querying. The module does not run migrations or synchronize the schema. Use your project's Drizzle migration workflow; for this small example, the equivalent SQL is:

CREATE TABLE users (
  id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name text NOT NULL
);

Pass the same schema to drizzleConfig.schema and NodePgDatabase<typeof schema>: the first configures Drizzle at runtime, while the second gives your injected database its query types. Table imports also work with Drizzle's select, insert, update, and delete builders.

Asynchronous configuration

Use registerAsync() when options depend on another Nest provider. Install @nestjs/config for this example, and place the registration in your module's imports:

import { ConfigModule, ConfigService } from "@nestjs/config";
import { DrizzlePgModule } from "nestjs-drizzle-pg";
import * as schema from "./schema";

DrizzlePgModule.registerAsync({
  imports: [ConfigModule.forRoot()],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    pgConfig: {
      type: "pool" as const,
      config: {
        connectionString: config.getOrThrow<string>("DATABASE_URL"),
        max: 10,
        connectionTimeoutMillis: 5_000,
      },
    },
    drizzleConfig: { schema },
  }),
});

The factory may also return a promise. registerAsync() supports useClass and useExisting factories with a create() method returning DrizzlePgModuleOptions or a promise of those options.

Multiple databases

Give each additional registration a distinct alias. Both alias and isGlobal belong at the top level of the registration, including for registerAsync(); they do not belong inside its useFactory result.

import { Injectable, Module } from "@nestjs/common";
import { sql } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import type { Client } from "pg";
import {
  DrizzlePgModule,
  DrizzlePgService,
  InjectDrizzlePg,
  InjectDrizzlePgService,
  InjectPgConnection,
} from "nestjs-drizzle-pg";

@Injectable()
export class AnalyticsService {
  constructor(
    @InjectDrizzlePg() private readonly primary: NodePgDatabase,
    @InjectDrizzlePg("analytics") private readonly analytics: NodePgDatabase,
    @InjectPgConnection("analytics") private readonly raw: Client,
    @InjectDrizzlePgService("analytics")
    private readonly health: DrizzlePgService,
  ) {}

  databaseTimes() {
    return Promise.all([
      this.primary.execute(sql`select current_timestamp as time`),
      this.analytics.execute(sql`select current_timestamp as time`),
    ]);
  }

  rawQuery() {
    return this.raw.query("select current_database() as database");
  }

  isReachable() {
    return this.health.ping();
  }
}

@Module({
  imports: [
    DrizzlePgModule.register({
      pgConfig: {
        type: "pool",
        config: { connectionString: process.env.DATABASE_URL, max: 10 },
      },
    }),
    DrizzlePgModule.registerAsync({
      alias: "analytics",
      useFactory: async () => ({
        pgConfig: {
          type: "client" as const,
          config: { connectionString: process.env.ANALYTICS_DATABASE_URL },
        },
      }),
    }),
  ],
  providers: [AnalyticsService],
  exports: [AnalyticsService],
})
export class AnalyticsModule {}

Set both database URLs for this example. The raw connection, Drizzle database and health service all use the same connection within their registration; different aliases use independent clients or pools. Match the injected raw type to its pgConfig.type: Client for a client, Pool for a pool.

Omitted, empty ("") and "default" aliases identify the default registration. Inject its database with @InjectDrizzlePg(), raw connection with @InjectPgConnection(), and health service with @InjectDrizzlePgService() or directly as DrizzlePgService. getDrizzlePgServiceToken() now resolves these default aliases to that service class. Existing named getDrizzlePgServiceToken("analytics") tokens and @InjectDrizzlePg(alias) remain supported. Register once per alias; use another alias for a different database or connection configuration.

Modules are local by default. Set isGlobal: true only when the registered providers should be available throughout the application; otherwise import the registration into the module containing its consumers, or re-export it through a shared module.

Connection lifecycle

| Configuration | Startup behavior | When to use it | | -------------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | pgConfig: { type: "pool", config } | Creates a pool; database connections are acquired as queries run. | Application services serving concurrent requests. | | pgConfig: { type: "client", config } | Connects one client during Nest initialization; connection errors fail startup. | A module that deliberately uses a single PostgreSQL session. | | Omit pgConfig | Creates and connects a client using node-postgres environment defaults. | Applications already configured through PGHOST, PGPORT, PGUSER, PGPASSWORD, and PGDATABASE. |

config is passed to node-postgres as PoolConfig or ClientConfig, including connection strings, TLS and timeout options. A lazy pool does not prove database availability at startup: execute a query or call the registered DrizzlePgService.ping() when your readiness policy requires a check. ping() returns false if its query fails.

The module creates and owns its connections; registering an existing external pool/client is not supported. Each registered connection is ended by the module's shutdown hook. Do not manually call end() on a module-owned connection during normal operation.

Enable Nest shutdown hooks in your application's existing bootstrap so process signals trigger cleanup:

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableShutdownHooks();
  await app.listen(3000);
}

void bootstrap();

For tests or standalone application contexts, call app.close() / module.close() when finished. If a client fails to connect during initialization, the module attempts to close it before propagating the startup error.

Named-token compatibility

Named database, connection and service tokens now use separate role namespaces so aliases such as CONNECTION_primary, SERVICE_primary and names containing colons cannot collide. Use getDrizzlePgToken(alias), getPgConnectionToken(alias) and getDrizzlePgServiceToken(alias), or the corresponding injection decorators, instead of constructing token strings. Hard-coded named token strings from earlier releases must be replaced with these helpers. Unnamed, empty and "default" aliases retain their existing default tokens.

API reference

| API | Purpose | | ---------------------------------------- | ---------------------------------------------------------------- | | DrizzlePgModule.register(options) | Register connection and Drizzle options synchronously. | | DrizzlePgModule.registerAsync(options) | Build options through Nest dependency injection. | | @InjectDrizzlePg(alias?) | Inject a Drizzle database; defaults to the unnamed registration. | | getDrizzlePgToken(alias?) | Get the database token for @Inject() or module lookups. | | DrizzlePgService.ping() | Execute SELECT 1 and return a Promise<boolean>. | | @InjectPgConnection(alias?) | Inject the module-owned raw Client or Pool. | | getPgConnectionToken(alias?) | Get the exported raw-connection token. | | @InjectDrizzlePgService(alias?) | Inject the health and lifecycle service. | | getDrizzlePgServiceToken(alias?) | Get the service token; defaults resolve to DrizzlePgService. | | drizzleConfig | Forward Drizzle configuration, such as schema and logger. | | alias / isGlobal | Choose a registration name and whether its providers are global. |

Use Drizzle itself for queries, transactions and migrations. This package supplies Nest registration, injection and connection lifecycle management.

Project

Report an issue · Contributing · License