Package Exports
- drizzle-pgmem
Readme
drizzle-pgmem
Tiny utilities to smooth Drizzle ORM on top of pg-mem for fast in-memory testing.
Permanent shims (always on)
Applied automatically every time you call applyIntegrationsToPool (not user-configurable):
| Shim | Purpose |
|---|---|
rowMode shim |
Strips rowMode from query configs so pg-mem accepts queries Drizzle emits (esp. for destructured selects). |
getTypeParser shim |
Provides a minimal getTypeParser + internal _types holder and hides any query.types object so pg-mem won't throw. Required for newer Drizzle ORM + migrator code paths. |
These shims are foundational, always safe, and idempotent (calling applyIntegrationsToPool multiple times won't re‑wrap endlessly).
Optional integrations
Optional integrations can be selectively enabled or disabled. They are applied in a deterministic alphabetical order so wrapper layering is stable across runs.
| Integration Name | Description |
|---|---|
arrayRowToObject |
Converts array-mode or aliased rows into object-shaped rows, enabling property access by column name after destructuring. |
countToValue |
Normalizes COUNT(*) results from { count: number } to { value: string } for compatibility with some Drizzle patterns. |
randomFunctionPolyfill |
Provides missing PostgreSQL random() in pg-mem, installed lazily on first query. |
Deterministic apply order (alphabetical): arrayRowToObject, countToValue, randomFunctionPolyfill.
Install
npm install -D drizzle-pgmem drizzle-orm pg-memQuick start
// test/db.ts (or similar)
import { newDb } from 'pg-mem';
import { drizzle } from 'drizzle-orm/node-postgres';
import { applyIntegrationsToPool } from 'drizzle-pgmem';
export function makeMemDb() {
const db = newDb();
const { Pool } = db.adapters.createPg();
const pool = new Pool();
// Apply optional integrations (rowMode shim is always applied internally)
applyIntegrationsToPool(pool);
const d = drizzle(pool);
return { pool, db: d, close: () => pool.end() };
}Example: select a record doesn't fail anymore (thanks to built-in shim)
import { serial, text, pgTable } from 'drizzle-orm/pg-core';
import { makeMemDb } from './test/db';
const people = pgTable('people', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
});
const { db, pool } = makeMemDb();
await pool.query(`create table if not exists people (
id serial primary key,
name text not null
)`);
await db.insert(people).values({ name: 'Alice' });
const [record] = await db.select({ id: people.id }).from(people);
console.log(!!record); // true if a row existsExample: enable only a specific integration
import { newDb } from 'pg-mem';
import { drizzle } from 'drizzle-orm/node-postgres';
import { applyIntegrationsToPool } from 'drizzle-pgmem';
const db = newDb();
const { Pool } = db.adapters.createPg();
const pool = new Pool();
// Enable only the countToValue integration (others disabled)
applyIntegrationsToPool(pool, { activeIntegrations: ['countToValue'] });
const d = drizzle(pool);Active and inactive integrations
The following options allow you to control which optional integrations run. (Permanent shims always run.)
activeIntegrations: An array of optional integration names (IntegrationName[]) that explicitly enables only the listed integrations. Any optional integrations not listed here will be disabled.inactiveIntegrations: An array of optional integration names (IntegrationName[]) that explicitly disables the listed integrations. Any optional integrations not listed here will remain enabled by default.
Edge Cases:
- Precedence: If both
activeIntegrationsandinactiveIntegrationsare provided,activeIntegrationswins (others are ignored). - Empty Arrays:
activeIntegrations: []=> disable all optional integrations.inactiveIntegrations: []=> (same as omitting it) all optional integrations stay enabled.
- Omitted Options: If neither option is set, all optional integrations are enabled.
- Unknown Names: Silently ignored.
- Idempotent: Calling
applyIntegrationsToPoolmore than once is safe; previously wrapped pools won't double-wrap.
Common scenarios
Disable exactly one integration:
applyIntegrationsToPool(pool, { inactiveIntegrations: ['randomFunctionPolyfill'] });Disable all optional integrations (only permanent shims remain):
applyIntegrationsToPool(pool, { activeIntegrations: [] });Enable just two integrations:
applyIntegrationsToPool(pool, { activeIntegrations: ['arrayRowToObject', 'countToValue'] });Provide both (only activeIntegrations is considered):
applyIntegrationsToPool(pool, {
activeIntegrations: ['countToValue'],
inactiveIntegrations: ['arrayRowToObject', 'randomFunctionPolyfill'] // ignored
});TypeScript
Union of integration names is exported as IntegrationName:
import type { IntegrationName } from 'drizzle-pgmem';
// IntegrationName = 'arrayRowToObject' | 'countToValue' | 'randomFunctionPolyfill'Why alphabetical order?
Predictable layering prevents subtle differences in how wrappers compose (e.g. a row shape transformer should run before a count normalizer, etc.). If a new integration is added, the order remains stable because it's inserted by name.
FAQ
Q: Can I re-run applyIntegrationsToPool on the same pool?
A: Yes, it's safe; guards prevent duplicate wrapping.
Q: Why is getTypeParser permanent instead of optional?
A: Newer Drizzle code paths (including the migrator) call client.getTypeParser or use query.types. pg-mem doesn't implement this API and throws without the shim, so it's foundational for functionality—not a tweak.
Q: Does this change any production runtime behavior? A: No. It's intended for in-memory testing with pg-mem. The shims/polyfills only adapt incompatibilities; logic stays equivalent to a real Postgres + node-postgres environment where possible.