Skip to main content
Drizzle is a lightweight TypeScript ORM that stays close to SQL. Schema is defined in TypeScript files, queries look like SQL with type safety wrapped around them, and migrations are generated as plain .sql files you can inspect. For pooler-level constraints, see Connection pooling. For broader migration patterns, see Migrations.

Connection setup

Drizzle works with several Postgres drivers. The most-used pairing on Powabase is postgres.js (a.k.a. postgres), which needs one flag on the URL:
  • prepare=false disables postgres.js’s prepared-statement cache. Required for PgBouncer transaction-mode pooling.
You can also pass { prepare: false } directly in client options instead of the query param:

Schema as TypeScript

src/schema.ts:
Column names are snake_case in SQL; the TypeScript field names are camelCase. Both are explicit, no implicit conversion.

Queries

The queries read like SQL because that’s the design: db.select().from().where().orderBy().limit() maps 1:1 to SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT. Compared with raw SQL, Drizzle infers result types from the schema; compared with heavier ORMs, there’s no magic between you and the query plan.

Migrations with Drizzle Kit

drizzle.config.ts:
Migrations land in ./drizzle/ as .sql files (one per migration step) plus a _journal.json index. Inspect them before applying in production. Drizzle’s generated SQL is straightforward, but anything that touches data is worth a second look. The migration tracking table is drizzle.__drizzle_migrations. Don’t touch it.

RLS with Drizzle

Drizzle, like Prisma, connects as supabase_admin and bypasses RLS. To run a query under a specific user’s identity for RLS-gated reads:
Inside the transaction, RLS applies. Don’t issue the SET LOCAL outside a transaction. PgBouncer hands you a different server connection per statement, so your role and claims won’t persist. For most apps, the cleaner split is PostgREST (/rest/v1/*) under user JWTs for RLS-required reads from the browser, and Drizzle for server-side work under supabase_admin.

Drizzle in serverless

postgres.js opens a real socket per call. In Lambda / Vercel Functions, the right shape is one connection per invocation:
The max: 1 keeps each invocation to one connection. Without sql.end(), you’ll leak connections at PgBouncer until max_client_conn = 200 cluster-wide runs out and new invocations start 429ing. For high-traffic serverless workloads, consider Drizzle’s HTTP-based drivers (Neon serverless driver, Vercel Postgres). They’re built for stateless invocations and don’t burn pooler slots. For moderate serverless traffic on Powabase, the open-and-close pattern is fine.

Next steps

Connection pooling

Why prepare=false is required.

Migrations

The migration patterns across all three ORMs we cover.

Direct Postgres

For SQL Drizzle doesn’t cover: bulk imports, schema introspection.

Prisma

The heavier-but-more-batteries-included TypeScript alternative.