Database
Next.js boilerplate with Neon
Serverless Postgres you can branch like git, with a driver built for cold starts.
Serverless Postgres with copy-on-write database branching. It ships an HTTP driver for one-shot queries and a pooled connection for route handlers that need a session. It also adds a read-only db-inspector agent, and a Bash guard that keeps migrations off the pooler.
What Neon adds to the agent layer: 2 rules · 2 skills · 1 subagent · 1 hook · 6 solution docs · 1 MCP server
Maintained by @raviMITNext.js on Vercel
From the manifest
Should you pick Neon?
Pick it if
Teams that want plain Postgres plus a throwaway database per pull request, running on Vercel or another serverless host where connections are scarce.
Watch out for
- Standard Postgres. ORMs and psql work unchanged, with no custom query layer to learn.
- Branching is copy-on-write, so a preview branch of a 20 GB database only adds storage for what the branch changes.
Show 3 moreShow fewer
- Compute scales to zero after 5 idle minutes and wakes in a few hundred milliseconds. On low-traffic previews that shows up as a slow first request.
- Database only. No auth, storage, realtime or generated API. Pair it with Better Auth or Clerk. Supabase bundles those.
- Two connection strings to keep straight (pooled and direct). Point a migration at the pooled one and it fails in confusing ways.
What it costs
Free plan: up to 100 projects, each with 100 CU-hours a month and 0.5 GB of storage. Launch and Scale have no monthly minimum: you pay per CU-hour ($0.106 on Launch) plus $0.35 per GB-month of storage.
Prices change. Check with Neon before you commit.
registry/tested.yaml
Tested with Neon
Each pair was installed, typechecked, linted, built and booted together.
- Auth
- Better AuthClerk
- Admin panel
- Admin panel
- Error tracking
- Sentry
- Customer support
- Crisp
- Storage
- Cloudflare R2Vercel Blob
Not tested yet: Supabase Auth and Supabase Storage.
What it adds
What Neon adds to the repo
Read straight from the neon manifest, so it is exactly what lands in your repo.
Environment variables
DATABASE_URLRequired
Pooled connection string. Used by the app at runtime. The hostname contains "-pooler".
- Where to get it
- https://console.neon.tech - open your project, Connect, and copy the "Pooled connection" string.
- Placeholder
- postgresql://user:password@ep-example-123456-pooler.us-east-2.aws.neon.tech/neondb?sslmode=require
DATABASE_URL_UNPOOLEDOptional
Direct connection string, same host without "-pooler". Migrations, DDL and anything holding a session use this one. This is Neon's name for it and what the Vercel integration injects; an ORM that declares its own key for the same endpoint (Prisma reads DIRECT_URL) adds that key below - set every one of them to this exact string.
- Placeholder
- postgresql://user:password@ep-example-123456.us-east-2.aws.neon.tech/neondb?sslmode=require
NEON_LOCAL_PROXYOptional
Local development without a Neon account. Set it to localhost:4444, point DATABASE_URL at a Postgres on this machine, and run
bun run db:proxyin another terminal. The Neon driver then talks to that proxy instead of Neon's cloud. Leave it empty against a real Neon branch. The app refuses to start with it set on a Vercel production deployment.- Where to get it
- See the "Develop against a local Postgres" step in docs/onboard.md.
- Placeholder
NEON_API_KEYOptional
Personal or org API key. Only needed for the neonctl CLI and the db:branch script. The app never reads it, and the MCP server signs in with OAuth.
- Where to get it
- https://console.neon.tech/app/settings/api-keys
- Placeholder
- neon_api_key_xxxxxxxxxxxxxxxxxxxx
Dependencies
- @neondatabase/serverless^1.0.0
- @types/pg^8.23.0dev
- pg^8.23.0dev
Scripts
- bun run db:branch
bun scripts/db-branch.ts
- bun run db:proxy
bunx tsx --env-file-if-exists=.env.local scripts/neon-local-proxy.ts
- bun run db:psql
psql "$(bunx neonctl connection-string)"
MCP server
neon
- URL
- https://mcp.neon.tech/mcp?readonly=true
Files it writes
5 files, at these exact paths.
scripts/2 files
- db-branch.ts
- neon-local-proxy.ts
src/3 files
db/3 files
- client.ts
- health.ts
- neon-local.ts
Stack slots it fills
The stack declares these injection points; this battery supplies the fragment, so the provider tree, the env check and the schema stay one file each instead of many.
- @slot db-client
- @slot env-required
- @slot hook-probes
- @slot legal-processors
- @slot verify-checks
The differentiator
What Neon teaches your agent
Other starter kits stop at the package. This is the part an agent reads: where it may work, what it must never do there, and the problems someone already solved.
Rules (2)
Loaded when the agent opens a matching file.
One connection surface, and the right driver for the job
Loads onsrc/db/**src/app/api/**.claude/rules/neon-connections.md
Database access goes through src/db - getSql(), getPool(), withTransaction(),
batchTransaction(). Nothing else in the repo constructs a client.
Required
- Import from
@/db(or the ORM client bound togetPool()). Never callneon()ornew Pool()in a route handler, a server action, a component or a script; every extra instance is another set of sockets against the same pooler. - Default to
getSql(). The HTTP driver has no connection to establish, which is why it wins on a cold serverless instance. One statement per call. - Use
getPool()/withTransaction()only when a single request genuinely needs a shared session: an interactive transaction,set local, an advisory lock, a cursor. Keep the transaction body free of HTTP calls - it holds a connection for its whole duration. - Use
batchTransaction()when several statements must commit together but none of them reads a previous statement's result. It is one round trip and holds nothing. - Parameterise everything. The tagged template (
sql`select * from users where id = ${id}`) sends values out of band; string concatenation into SQL is an injection bug even when the value "comes from our own code".
Forbidden
DATABASE_URLin client components,NEXT_PUBLIC_*anything database-shaped, or a connection string in a log line, an error message returned to the browser, or a commit. The env-leak hook catches the obvious shapes; do not rely on it.- Long-lived state assumed across requests. Neon branches scale to zero and serverless instances are recycled, so treat every request as if the connection is new. Nothing may be cached on the pool object.
select *in a hot path, and unboundedselectwithoutlimit. Egress and row-parse time are the two costs you notice on a serverless database.- Swallowing connection errors. A
Connection terminatedortoo many connectionsmust surface - it is the signal that the pool policy is wrong, and hiding it turns a 30-second incident into a week of mystery latency.
Reads that can be stale
Wrap them in unstable_cache or a route segment revalidate before reaching for a read replica. Most "we need a replica" traffic is the same three queries running on every request.
Local Postgres
- Local development runs the same Neon driver through
bun run db:proxyandNEON_LOCAL_PROXY.databaseUrl()insrc/db/client.tsapplies it before the first query, so no code path forks. Never swap inpg,postgresordrizzle-orm/node-postgres"just for local": that client can rundb.transaction()and the HTTP driver in production cannot, so local runs would pass code that breaks on deploy. NEON_LOCAL_PROXYnever goes into a Vercel environment. The app throws on a production deployment that has it set.
Schema changes go through ORM migration files on the direct URL
Loads onsrc/db/**.claude/rules/neon-migrations.md
Every schema change in this repo is a migration file produced by the ORM, applied
with the ORM's own command, against DATABASE_URL_UNPOOLED.
Required
- Generate the migration (
bun run db:generatefor Drizzle,prisma migrate dev --name <change>for Prisma), read the emitted SQL, then apply it. The generated file is the reviewable artefact - a schema change with no file in the diff did not happen. - Apply migrations with
bun run db:migrate. It has to reach the direct endpoint, never the-poolerone. How it gets there is the ORM's business - Prisma'sprisma.config.tsreadsDIRECT_URL, thenDATABASE_URL_UNPOOLED; Drizzle derives the direct host fromDATABASE_URL- so set every direct-connection key.env.examplelists to the same string and let the ORM pick. - In a script you write, resolve the direct string with
directDatabaseUrl()fromsrc/db/client.tsrather than re-readingprocess.env: it refuses to hand back a pooler host instead of letting the migration discover that itself. - Edit table definitions only in the ORM schema file -
src/db/schema.tsunder Drizzle,prisma/schema.prismaunder Prisma. That file is owned by the ORM battery. - Test destructive migrations on a Neon branch first:
bun run db:branch, apply there, check the row counts, then apply tomain.
Forbidden
- Raw DDL from application code, a script,
psql -c,neonctlor the Neon SQL editor.create table,alter table,drop,truncateandcreate indexoutside a migration file put the database and the schema file out of sync, and the next generated migration will try to "fix" the drift by dropping your column. - Pointing any migration at the pooled URL. PgBouncer in transaction mode hands each statement to a different backend, so
create index concurrentlyfails, advisory locks silently do nothing, and multi-statement DDL half-applies. Theguard-neon-sqlhook blocks the obvious cases; do not work around it by inlining the string. drizzle-kit pushorprisma db pushagainst anything but your own throwaway branch. Both skip the migration history, so the change exists in the database and nowhere in git.- Hand-editing a migration that has already been applied to
main. Write a new one.
Data migrations
Keep DDL and backfills in separate migrations. Add the nullable column, deploy, backfill in batches, then a second migration adds the not null. A single migration that adds a column and rewrites ten million rows holds a lock for the length of the rewrite, and on a serverless host the request that triggered it times out first.
Skills (2)
Invoked by name.
- /db-branch
Create, use, reset and delete Neon database branches, for a feature branch, a preview deploy, a migration rehearsal or a point-in-time investigation.
.claude/skills/db-branch/SKILL.md
- /migrate-on-neon
Run a schema migration against Neon safely, on the direct URL, rehearsed on a branch first, with a recovery path when it fails halfway.
.claude/skills/migrate-on-neon/SKILL.md
Subagents (1)
db-inspector
Read-only Neon Postgres inspector. Explains schema, data shape and query plans. Runs SELECT and EXPLAIN only, and refuses every statement that writes or changes schema.
ToolsReadGrepGlobBashmcp__neon
Hooks (1)
The repo wires hooks for Claude Code only.
- guard-neon-sql
Blocks raw DDL through psql, migrations that would really run through the Neon pooler, and schema pushes that skip migration files. The repo's own db:migrate scripts pass.
PreToolUseBash
Solution docs (6)
Written before you hit the problem. Each one ships in the repo at docs/solutions/ and is published here as a cookbook page.
- A Neon branch per preview deploymentPreview deploys that share the production database corrupt it or lie to you. Give every preview its own copy-on-write Neon branch, wired to the deployment's environment variables.docs/solutions/neon/branch-per-preview-deployment.md
- Neon cold starts, where the half second goes and what to do about itScale-to-zero means an idle branch takes roughly 500 ms to wake, and a serverless function adds its own cold start on top. How to measure the parts and fix the ones that matter.docs/solutions/neon/cold-start-latency.md
- Connection exhaustion on serverless Postgres, and how to actually fix itServerless does not queue requests on a pool, it creates pools. Here is the arithmetic, the four real causes, and the fix for each.docs/solutions/neon/connection-exhaustion-in-serverless.md
- Local Postgres with the Neon serverless driver, no Neon accountThe Neon driver speaks HTTPS and WebSocket, not the Postgres wire protocol. A small local proxy plus two neonConfig settings let it run against a Postgres on your laptop, with no code fork.docs/solutions/neon/local-postgres-without-a-neon-account.md
- Running migrations on Vercel without a half-applied schemaVercel has no migration step, so people add one in the wrong place. Where migrations belong in the build, why the direct URL is mandatory, and how to deploy a breaking change in two safe halves.docs/solutions/neon/migrations-on-vercel.md
How it fits
What Neon needs, and what it goes well with
The resolver enforces this before it generates anything, and names every addition it makes.
Requires
- An ORM battery. The resolver adds the default one for you and tells you why.
Pairs well with
Nothing extra. Add any tested battery alongside Neon.
Cannot be combined with
Compared with the alternatives
Build a repo with Neon
Free and MIT. The builder opens with Neon picked. You download the zip right away, and we email you the link too.
Presets
Presets that already include Neon
A tested selection with its own file tree and its own generated CLAUDE.md. Start from one instead of from blank.