Skills api-database-vercel-postgres
Serverless PostgreSQL on Vercel with edge-compatible SDK
git clone https://github.com/agents-inc/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/agents-inc/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/dist/plugins/api-database-vercel-postgres/skills/api-database-vercel-postgres" ~/.claude/skills/agents-inc-skills-api-database-vercel-postgres && rm -rf "$T"
dist/plugins/api-database-vercel-postgres/skills/api-database-vercel-postgres/SKILL.mdVercel Postgres Patterns
Quick Guide:
is a thin wrapper around@vercel/postgresthat auto-connects from@neondatabase/serverlessenv vars. Use thePOSTGRES_URLtagged template for one-shot queries (edge-compatible, auto-pooled). Usesqlto get a client for multi-query sequences. On edge runtimes, connections cannot be reused between requests (sql.connect()). This package is deprecated (Dec 2024) -- for new projects, usemaxUses: 1directly.@neondatabase/serverless
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use the
tagged template for one-shot queries -- it auto-connects from sql
and handles pooling)POSTGRES_URL
(You MUST call
after using a client obtained from client.release()
or sql.connect()
-- leaked clients exhaust the connection pool)pool.connect()
(You MUST use
for pooled connections and POSTGRES_URL
for direct connections -- mixing them causes connection exhaustion or feature limitations)POSTGRES_URL_NON_POOLING
(You MUST acknowledge that
is deprecated -- recommend @vercel/postgres
for new projects)@neondatabase/serverless
</critical_requirements>
Auto-detection: @vercel/postgres, vercel postgres, POSTGRES_URL, POSTGRES_URL_NON_POOLING, sql tagged template vercel, createPool vercel, createClient vercel, VercelPool, VercelClient
When to use:
- Maintaining existing projects that already use
@vercel/postgres - Querying Postgres from edge/serverless functions on Vercel
- Simple database access with auto-connection from environment variables
- Migrating away from
to@vercel/postgres@neondatabase/serverless
Key patterns covered:
tagged template (auto-pooled, edge-compatible, one-shot queries)sql
for multi-query client sessionssql.connect()
/createPool()
for custom configurationscreateClient()- Environment variables (
,POSTGRES_URL
)POSTGRES_URL_NON_POOLING - Edge vs Node.js runtime differences
- Migration path to
@neondatabase/serverless
When NOT to use:
- New projects (use
directly)@neondatabase/serverless - Long-lived server processes with persistent connections (use standard
driver)pg - General PostgreSQL query syntax (use a SQL/Postgres skill)
Detailed Resources:
- For decision frameworks and quick lookup tables, see reference.md
Examples:
- examples/core.md -- sql tagged template, createPool, createClient, edge patterns, migration
<philosophy>
Philosophy
@vercel/postgres is a convenience wrapper around @neondatabase/serverless that simplifies connection management for Vercel-deployed applications. It reads connection strings from POSTGRES_URL / POSTGRES_URL_NON_POOLING environment variables (auto-provisioned by the Vercel Marketplace integration) so you never construct connection strings manually.
Core principles:
- Zero-config connections -- The
export auto-connects from environment variables. No connection string setup needed in code.sql - Tagged template safety --
is a tagged template literal, not a function. Parameters are auto-parameterized, preventing SQL injection.sql - Pooling by default --
andsql
use the pooled connection string (createPool()
).POSTGRES_URL
uses the direct string (createClient()
).POSTGRES_URL_NON_POOLING - Edge-aware -- On edge runtimes, the SDK sets
because IO connections cannot survive between requests. For multi-query in a single request, usemaxUses: 1
.sql.connect()
Deprecation context:
Vercel Postgres was sunset in December 2024. All databases were migrated to Neon. The
@vercel/postgres npm package (v0.10.0) is no longer maintained. Migration path:
- Full migration (recommended):
(actively developed, richer API with HTTP transactions and composable fragments)@neondatabase/serverless
<patterns>
Core Patterns
Pattern 1: One-Shot Queries with sql
sqlThe
sql export is a tagged template that auto-connects from POSTGRES_URL. Values are auto-parameterized (preventing SQL injection). See examples/core.md for full examples with good/bad comparisons.
import { sql } from "@vercel/postgres"; const ACTIVE_STATUS = "active"; const { rows } = await sql`SELECT id, name FROM users WHERE status = ${ACTIVE_STATUS}`;
Pattern 2: Multi-Query Sessions with sql.connect()
sql.connect()When you need multiple queries on the same connection (transactions, sequential operations), obtain a client. Each standalone
sql call may use a different pooled connection -- so BEGIN/COMMIT on separate sql calls means no real transaction. See examples/core.md for transaction patterns.
const client = await sql.connect(); try { await client.sql`BEGIN`; // ... queries on same client ... await client.sql`COMMIT`; } catch (error) { await client.sql`ROLLBACK`; throw error; } finally { client.release(); }
Pattern 3: Custom Pool and Client
createPool() for custom connection strings (secondary databases). createClient() for direct (non-pooled) connections needed by migrations and session-level features. See examples/core.md for full examples.
import { createPool } from "@vercel/postgres"; const pool = createPool({ connectionString: process.env.SECONDARY_POSTGRES_URL, }); const { rows } = await pool.sql`SELECT id, title FROM posts WHERE published = true`;
Pattern 4: Edge Runtime Considerations
On edge runtimes, the SDK sets
maxUses: 1 -- connections cannot be reused between requests. Single sql calls work fine, but for multiple queries use sql.connect() to share one connection. See examples/core.md for edge-specific patterns.
Pattern 5: Migration to @neondatabase/serverless
@neondatabase/serverlessSince
@vercel/postgres is deprecated, migrate to @neondatabase/serverless. See examples/core.md for full migration examples.
Key differences to be aware of:
returns@vercel/postgres
--{ rows, rowCount, ... }@neondatabase/serverless
returns rows directly (unlessneon()
)fullResults: true
reads@vercel/postgres
--POSTGRES_URL
requires explicit connection string (typically@neondatabase/serverless
)DATABASE_URL
adds HTTP transactions via@neondatabase/serverless
and composable fragmentssql.transaction()
<decision_framework>
Decision Framework
Which API to Use
What kind of operation? +-- Single query (SELECT, INSERT, UPDATE, DELETE) | +-- Use sql tagged template directly +-- Multiple queries that must be atomic (transaction)? | +-- Use sql.connect() to get a client, wrap in BEGIN/COMMIT +-- Need custom connection string (not POSTGRES_URL)? | +-- Use createPool() with explicit connectionString +-- Need session-level features (SET, LISTEN/NOTIFY)? | +-- Use createClient() (reads POSTGRES_URL_NON_POOLING) +-- Starting a new project? +-- Use @neondatabase/serverless instead
Environment Variable Selection
What is the workload? +-- Serverless/edge function --> POSTGRES_URL (pooled) +-- Application queries --> POSTGRES_URL (pooled) +-- Schema migrations --> POSTGRES_URL_NON_POOLING (direct) +-- LISTEN/NOTIFY --> POSTGRES_URL_NON_POOLING (direct) +-- pg_dump / pg_restore --> POSTGRES_URL_NON_POOLING (direct)
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Using
for transactions withoutsql
-- Eachsql.connect()
tagged template call may use a different pooled connection. BEGIN on one connection and COMMIT on another means no transaction at all.sql - Forgetting
afterclient.release()
-- Leaked clients exhaust the connection pool, causing all subsequent queries to hang until timeout.sql.connect() - Using
for migrations -- The pooled connection runs through PgBouncer in transaction mode, which breaks session-level features needed by migration tools.POSTGRES_URL
Medium Priority Issues:
- String interpolation instead of tagged template --
is safe.sql`...${value}...`
...${value}...`)` is SQL injection.sql.query(\ - Creating pools/clients without closing them --
requires explicitcreateClient()
. Forgetting it leaks connections.client.end() - Ignoring deprecation --
v0.10.0 is the last version. No security patches or bug fixes will be released.@vercel/postgres
Gotchas & Edge Cases:
- Edge runtime
-- On edge, the pool cannot reuse connections within a request. If you fire multiplemaxUses: 1
calls, each opens a new connection. Usesql
to share one.sql.connect()
is a tagged template, not a function --sql
is wrong.sql(...)
is correct. This is a common error when copying from non-Vercel Postgres examples.sql`...`
vsPOSTGRES_URL
--DATABASE_URL
reads@vercel/postgres
by default.POSTGRES_URL
reads nothing by default (pass explicitly). After Neon migration, Vercel sets both, but your code must match the SDK's expectation.@neondatabase/serverless- PgBouncer transaction mode limitations -- Through pooled connections: no SET/RESET, no LISTEN/NOTIFY, no temporary tables with PRESERVE, no session-level advisory locks.
- Result shape differs from
--@neondatabase/serverless
returns@vercel/postgres
. The Neon{ rows, rowCount, fields }
function returns rows directly. This breaks code during migration if not accounted for.neon()
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use the
tagged template for one-shot queries -- it auto-connects from sql
and handles pooling)POSTGRES_URL
(You MUST call
after using a client obtained from client.release()
or sql.connect()
-- leaked clients exhaust the connection pool)pool.connect()
(You MUST use
for pooled connections and POSTGRES_URL
for direct connections -- mixing them causes connection exhaustion or feature limitations)POSTGRES_URL_NON_POOLING
(You MUST acknowledge that
is deprecated -- recommend @vercel/postgres
for new projects)@neondatabase/serverless
Failure to follow these rules will cause connection pool exhaustion, SQL injection vulnerabilities, or silent transaction failures.
</critical_reminders>