The officially supported PostgreSQL database adapter for Payload CMS with Drizzle ORM integration
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The officially supported PostgreSQL database adapter for Payload CMS. This package provides a complete database abstraction layer using Drizzle ORM to handle PostgreSQL database operations including CRUD operations, migrations, schema management, transactions, and versioning.
npm install @payloadcms/db-postgresimport { postgresAdapter, sql, geometryColumn } from "@payloadcms/db-postgres";
import type { PostgresAdapterArgs, PostgresAdapter, GeneratedDatabaseSchema, MigrateUpArgs, MigrateDownArgs } from "@payloadcms/db-postgres";For migration utilities:
import { migratePostgresV2toV3 } from "@payloadcms/db-postgres/migration-utils";For Drizzle ORM integration:
import { eq, and, or } from "@payloadcms/db-postgres/drizzle";
import { pgTable, text, integer } from "@payloadcms/db-postgres/drizzle/pg-core";
import { drizzle } from "drizzle-orm/node-postgres";import { buildConfig } from 'payload';
import { postgresAdapter } from '@payloadcms/db-postgres';
export default buildConfig({
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI,
},
// Optional configuration
idType: 'serial', // or 'uuid'
schemaName: 'my_schema', // experimental
extensions: ['uuid-ossp', 'postgis'],
}),
// ...rest of config
});The @payloadcms/db-postgres adapter is built around several key components:
postgresAdapter() function creates a configured database adapter instanceMain factory function and configuration for creating PostgreSQL database adapters with comprehensive features including connection pooling, schema management, and transaction support.
function postgresAdapter(args: PostgresAdapterArgs): DatabaseAdapterObj<PostgresAdapter>;
interface PostgresAdapterArgs {
/** PostgreSQL connection pool configuration (required) */
pool: PoolConfig;
/** Primary key type for generated IDs */
idType?: 'serial' | 'uuid';
/** Database schema name (experimental) */
schemaName?: string;
/** PostgreSQL extensions to enable */
extensions?: string[];
/** Directory containing migration files */
migrationDir?: string;
/** Allow custom ID values in create operations */
allowIDOnCreate?: boolean;
/** Store blocks as JSON instead of relational structure */
blocksAsJSON?: boolean;
/** Disable automatic database creation */
disableCreateDatabase?: boolean;
/** Suffix for locale tables */
localesSuffix?: string;
/** Suffix for relationship tables */
relationshipsSuffix?: string;
/** Suffix for version tables */
versionsSuffix?: string;
/** Filter specific tables */
tablesFilter?: string[];
/** Read replica connection strings */
readReplicas?: string[];
/** Transaction configuration or false to disable */
transactionOptions?: false | PgTransactionConfig;
/** Schema transformation hooks before initialization */
beforeSchemaInit?: PostgresSchemaHook[];
/** Schema transformation hooks after initialization */
afterSchemaInit?: PostgresSchemaHook[];
/** Drizzle query logger configuration */
logger?: DrizzleConfig['logger'];
/** Custom pg dependency injection */
pg?: typeof import('pg');
/** Enable schema pushing in development */
push?: boolean;
/** Production migration definitions */
prodMigrations?: Array<{
name: string;
up: (args: MigrateUpArgs) => Promise<void>;
down: (args: MigrateDownArgs) => Promise<void>;
}>;
}Raw SQL query builder for complex database operations that need to bypass the ORM.
const sql: SQL;Usage Example:
import { sql } from "@payloadcms/db-postgres";
// Raw SQL queries in Payload hooks
const result = await payload.db.drizzle.execute(sql`
SELECT * FROM users
WHERE created_at > ${new Date('2023-01-01')}
`);Helper function for creating PostGIS geometry columns for spatial data.
function geometryColumn(
name: string,
type: 'POINT' | 'LINESTRING' | 'POLYGON' | 'MULTIPOINT' | 'MULTILINESTRING' | 'MULTIPOLYGON',
srid?: number
): PgGeometry;Usage Example:
import { geometryColumn } from "@payloadcms/db-postgres";
import { pgTable, text, uuid } from "@payloadcms/db-postgres/drizzle/pg-core";
const locations = pgTable('locations', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
point: geometryColumn('point', 'POINT', 4326),
});Migration helper functions for upgrading PostgreSQL schemas from older versions of Payload.
function migratePostgresV2toV3(args: MigrateUpArgs): Promise<void>;Complete Drizzle ORM functionality re-exported for direct database operations, schema definitions, and raw SQL queries.
// Core Drizzle exports
const sql: SQL;
function eq<T>(left: T, right: T): SQLWrapper;
function and(...conditions: SQLWrapper[]): SQLWrapper;
function or(...conditions: SQLWrapper[]): SQLWrapper;
// PostgreSQL-specific exports
function pgTable(name: string, columns: Record<string, any>): PgTable;
function text(name?: string): PgText;
function integer(name?: string): PgInteger;
function pgEnum<T extends string>(name: string, values: readonly [T, ...T[]]): PgEnum<T>;
function pgSchema(name: string): PgSchema;
// Node.js PostgreSQL driver integration
function drizzle(options: DrizzleConfig): NodePgDatabase;interface PostgresAdapter extends BasePostgresAdapter {
drizzle: Drizzle;
pg: typeof import('pg');
pool: Pool;
poolOptions: PoolConfig;
}
interface GeneratedDatabaseSchema {
schemaUntyped: Record<string, unknown>;
}
type Drizzle =
| NodePgDatabase<ResolveSchemaType<GeneratedDatabaseSchema>>
| PgWithReplicas<PgDatabase<PgQueryResultHKT, Record<string, unknown>, ExtractTablesWithRelations<Record<string, unknown>>>>;
interface DatabaseAdapterObj<T> {
name: string;
allowIDOnCreate: boolean;
defaultIDType: 'number' | 'text';
init: (options: { payload: Payload }) => T;
}
interface PoolConfig {
connectionString?: string;
host?: string;
port?: number;
user?: string;
password?: string;
database?: string;
ssl?: boolean | object;
max?: number;
min?: number;
idle?: number;
acquire?: number;
evict?: number;
}
interface PgTransactionConfig {
isolationLevel?: 'read uncommitted' | 'read committed' | 'repeatable read' | 'serializable';
accessMode?: 'read only' | 'read write';
deferrable?: boolean;
}
type PostgresSchemaHook = (args: { schema: Record<string, unknown> }) => Record<string, unknown>;
interface MigrateUpArgs {
/** The Postgres Drizzle instance for executing SQL directly within the current transaction */
db: PostgresDB;
/** The Payload instance for executing Local API methods */
payload: Payload;
/** The PayloadRequest object containing the current transaction */
req: PayloadRequest;
}
interface MigrateDownArgs {
/** The Postgres Drizzle instance for executing SQL directly within the current transaction */
db: PostgresDB;
/** The Payload instance for executing Local API methods */
payload: Payload;
/** The PayloadRequest object containing the current transaction */
req: PayloadRequest;
}
// External types from dependencies (for reference)
interface Payload {
/** Payload CMS instance - see Payload CMS documentation */
}
interface PayloadRequest {
/** Payload request object containing transaction context - see Payload CMS documentation */
}
interface PostgresDB {
/** PostgreSQL database instance from Drizzle ORM - see Drizzle ORM documentation */
}
interface BasePostgresAdapter {
/** Base PostgreSQL adapter interface from @payloadcms/drizzle package */
}
type Pool = import('pg').Pool;
type NodePgDatabase<T> = import('drizzle-orm/node-postgres').NodePgDatabase<T>;
type PgWithReplicas<T> = import('drizzle-orm/pg-core').PgWithReplicas<T>;
type PgDatabase<A, B, C> = import('drizzle-orm/pg-core').PgDatabase<A, B, C>;
type PgQueryResultHKT = import('drizzle-orm/pg-core').PgQueryResultHKT;
type ExtractTablesWithRelations<T> = import('drizzle-orm').ExtractTablesWithRelations<T>;
type ResolveSchemaType<T> = 'schema' extends keyof T ? T['schema'] : GeneratedDatabaseSchema['schemaUntyped'];