codexmachina
Database

Every verified stack backed by Postgres (Neon).

Postgres on Neon via Drizzle ORM and the postgres-js driver.

400 verified stacks400 greenLast updated 2026-08-23How we verify →

400 verified stacks

Showing 12 across 12 app types. Every stack page links its neighbours along each axis, so any combination is a click or two from here.

AI WrapperNext.js 16 (App Router) · Auth.js (NextAuth)
Blog / CMSNext.js 16 (App Router) · Auth.js (NextAuth) · Resend
Booking / schedulingNext.js 16 (App Router) · Better Auth
CRMNext.js 16 (App Router) · Auth.js (NextAuth)
E-commerce storeNext.js 16 (App Router) · Auth.js (NextAuth)
Fintech ledgerNext.js 16 (App Router) · Better Auth · Resend
Fitness trackerNext.js 16 (App Router) · Auth.js (NextAuth)
Forum / communityNext.js 16 (App Router) · Auth.js (NextAuth) · Resend
Helpdesk / supportNext.js 16 (App Router) · Auth.js (NextAuth)
IoT telemetryNext.js 16 (App Router) · Auth.js (NextAuth) · Resend
Job boardNext.js 16 (App Router) · Auth.js (NextAuth) · Resend
LMS (learning platform)Next.js 16 (App Router) · Auth.js (NextAuth)

What Postgres (Neon) gives you

Postgres here is Neon reached through postgres-js, with Drizzle's pg-core dialect on top: drizzle({ client }) over a single module-level postgres(DATABASE_URL, { prepare: false }). That flag is not a preference. Neon's pooled (-pooler) endpoint is PgBouncer in transaction mode, where a backend is handed to a different session between statements, so server-side prepared statements break across the pool — and the same constraint is why this axis pairs with Drizzle rather than Prisma. One client per module is enough: PgBouncer and the runtime do the pooling, so there is no globalThis singleton dance. The schemas built on this dialect make three recurring type decisions. Primary keys are uuid(...).primaryKey().defaultRandom(), so ids come from the database. Timestamps are timestamp(..., { withTimezone: true }).defaultNow() — timestamptz, an absolute instant.

Closed value sets are text plus a CHECK constraint rather than pgEnum, so shipping a new role or subscription status is an ordinary constraint change instead of an ALTER TYPE migration. Counters are bigint({ mode: "number" }), and Better Auth's text user.id is referenced as text by the app tables rather than recast. Operationally, transaction-mode pooling forbids anything that spans statements on one backend: LISTEN/NOTIFY, session-scoped SET, advisory-lock sessions, WITH HOLD cursors. Those paths use Neon's direct endpoint instead. The connection client also changes with the deploy target — max: 1 per short-lived serverless instance, a real reused pool (max 10, idle_timeout 20) in a long-running Node process, and on Cloudflare Workers postgres-js is replaced outright by @neondatabase/serverless over HTTP, because Workers have no TCP sockets.

The capability that exists only on this side of the matrix is row-level security. Multi-tenant schemas ship ENABLE plus FORCE ROW LEVEL SECURITY with policies keyed on current_setting('app.current_org_id', true), which withTenant() sets per transaction — unset context yields no rows, so isolation fails closed inside the database rather than in application code. It requires a dedicated NOBYPASSRLS role: Neon's default neondb_owner carries BYPASSRLS, and connecting as it makes every policy silently inert.

Built for

Isolate tenants in the database

migrations/rls.sql enables and FORCEs row-level security per org-scoped table; every request's data access runs inside withTenant(db, orgId, tx => …), which set_configs app.current_org_id for that transaction. Unset context returns zero rows.

Add a value to a closed set

Columns like role and status are text with a CHECK constraint ("role in ('owner','admin','member')"), not pgEnum — a new value is a constraint change, with no ALTER TYPE migration to sequence.

Mint a primary key

uuid("id").primaryKey().defaultRandom() — the database generates the identifier, so an insert does not have to carry one and cross-table FKs are uuid-to-uuid.

Run the same schema on Workers

The edge deploy target swaps src/lib/db.ts for neon() from @neondatabase/serverless with drizzle-orm/neon-http: each query is a stateless HTTPS request, so there is no pool to size and no TCP socket to open.

Survive serverless fan-out

postgres(DATABASE_URL, { prepare: false, max: 1 }) per instance behind the -pooler endpoint. A large max multiplied by serverless concurrency exhausts the project's connections; PgBouncer, not the driver, does the multiplexing.

Why Postgres (Neon), specifically

note

prepare: false is mandatory — Neon's pooled endpoint is PgBouncer in transaction mode, where server-side prepared statements break across the pool.

note

Drizzle is paired here (not Prisma): Prisma's prepared-statement reliance is incompatible with transaction-mode pooling.

What Postgres (Neon) pulls in

bun add drizzle-orm postgres
DATABASE_URLNeon pooled (-pooler) connection string