Adapters / Drizzle · Postgres
Drizzle · Postgres
The reference adapter — raw SQL under a Drizzle-shaped schema, with an atomic FOR UPDATE SKIP LOCKED claim.
Setup
import { drizzleAdapter } from "easy-ping/adapters/drizzle";
database: drizzleAdapter(db, { prefix: "" }), // prefix is optional
db is a Drizzle instance over postgres-js — drizzle(postgres(connectionString)). Generate
the tables with createSchema() from the same import and push them with drizzle-kit, as shown
in the Quickstart.
Bootstrapping without Drizzle
renderPostgresDdl(coreSchema) emits CREATE TABLE IF NOT EXISTS statements for anyone not
using Drizzle for migrations. It's correct exactly once: on an existing database it's a silent
no-op, so it cannot pick up a column a later version adds. Read
Upgrading before you rely on it past the first deploy.
What it does under the hood
Nothing here changes how you use the adapter — it's the reason a second database adapter could be added without either one changing shape:
- Claiming is
FOR UPDATE SKIP LOCKED. Two concurrent cron sweeps step around each other instead of blocking, and it's proven against a conformance case that holds a real row lock on a separate connection and asserts the claim skips it within 2 seconds. - Dedupe relies on Postgres treating every
NULLas distinct — unlimited undeduped notifications (nodedupeKey) coexist under oneON CONFLICT (user_id, dedupe_key) DO NOTHING. - The adapter declares its own dialect to the plugin storage layer —
naming: "snake_case",serializesJson: true— rather than the storage layer assuming Postgres conventions. That declaration is what let MongoDB reuse the same plugin code unmodified.