Skip to content
Next.js+Vercel Postgres
Stack Integration

Next.js + Vercel Postgres Integration Guide

Vercel Postgres is a serverless PostgreSQL offering built for Next.js deployments — zero configuration, edge-compatible queries, and automatic connection pooling.

Use Cases
  1. Serverless database queries from Next.js Server Components and Route Handlers
  2. Edge-compatible SQL queries in Next.js Middleware
  3. Pooled connections handling Next.js serverless function cold starts
  4. Preview deployments with isolated Vercel Postgres branches
Implementation

Install @vercel/postgres and use the sql tagged template literal for parameterized queries — it handles connection pooling automatically. In Server Components, query directly without an explicit connection setup. For migrations, use a migration tool like node-postgres with a custom migration script or integrate Prisma targeting the Vercel Postgres connection string. Never use the direct (non-pooled) connection string from serverless functions — always use the pooled endpoint.

In detail

How the pieces connect

@vercel/postgres ships two entry points. The sql export reads POSTGRES_URL from the environment and talks to the pooled endpoint (PgBouncer in transaction mode) — that is what you import in Server Components and Route Handlers. The createClient/createPool helpers exist when you need an explicit client or to pass a different connection string. On Vercel, vercel env pull or the dashboard integration injects POSTGRES_URL (pooled), POSTGRES_URL_NON_POOLING (direct), and POSTGRES_PRISMA_URL automatically.

Data flow: a Server Component runs on the server, calls sql, the query goes through the pooler to Postgres, and the rendered HTML streams back. Nothing reaches the client. Because each serverless invocation is short-lived, the pooler — not your function — owns the long-lived TCP connections to the database.

Production gotchas

Pooled vs direct. Serverless functions spin up many concurrent instances. If each opened a direct connection you would exhaust Postgres' max_connections fast. Always query through POSTGRES_URL (pooled). Reserve POSTGRES_URL_NON_POOLING for migrations and schema changes, which need a stable session and don't run hot.

Transaction-mode limits. The pooler runs in transaction mode, so session-level features — LISTEN/NOTIFY, prepared statements held across statements, SET that must persist — don't behave as on a dedicated connection. Keep work inside a single statement or an explicit sql.begin-style transaction via the underlying client.

SQL injection. The sql tagged template parameterizes interpolated values. Building a query by string concatenation (sql.query(\... ${userInput}`)`) drops that safety. Pass values as template placeholders.

Caching. Next.js may cache fetch and route output. A sql call inside a cached Server Component can serve stale rows — set export const dynamic = 'force-dynamic' or revalidate deliberately for read-after-write paths.

When this combo fits — and when it doesn't

It fits when you are already deploying Next.js on Vercel and want relational data without standing up your own Postgres host or pooler. The zero-config env wiring, per-branch preview databases, and a pooler tuned for serverless remove most connection-management work, and you write plain parameterized SQL instead of an ORM if you prefer.

It fits less well when you need session-level Postgres features the transaction pooler can't expose, very high sustained write throughput where a dedicated managed instance is cheaper, or portability away from Vercel — @vercel/postgres reads Vercel's env conventions, and the underlying database is provisioned through Vercel's marketplace partners. If you are off Vercel or want a vendor-neutral driver, a standard node-postgres/postgres.js client against any Postgres URL is the more flexible choice.

Setup checklist

  1. npm i @vercel/postgres, then create or attach a Postgres store from the Vercel Storage tab. Vercel writes the env vars into the project.
  2. Pull them locally: vercel env pull .env.development.local so sql resolves POSTGRES_URL in dev.
  3. Query in a Server Component or Route Handler: import { sql } from '@vercel/postgres' then const { rows } = await sql\SELECT * FROM users WHERE id = ${id}``.
  4. Run migrations against POSTGRES_URL_NON_POOLING with node-postgres or Prisma (POSTGRES_PRISMA_URL), never the pooled URL.
  5. For preview deployments, confirm the integration provisions an isolated branch database so preview writes never touch production.
  6. Mark write-then-read routes dynamic = 'force-dynamic' to avoid serving cached rows.
Other integration guidesView all →
Related

Need this built?