关于 neon-vercel-postgres
面向在无服务器和边缘环境部署应用的开发者,介绍如何使用 Neon 或 Vercel Postgres。内容涵盖配置、安全查询、连接池、模式管理及部署注意事项。
主要功能
- 01Neon 与 Vercel 配置指南
- 02无服务器连接池
- 03SQL 模板标签查询方式
- 04Drizzle 与 Prisma 模式工作流
- 05事务和部署指南
适用场景
- 01为 Cloudflare Workers 连接 Postgres
- 02在 Vercel 构建 Next.js 应用
- 03避免无服务器环境连接耗尽
- 04创建模式并运行迁移
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill neon-vercel-postgres安装后在 Claude Code 中输入/neon-vercel-postgres确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.md.claude-plugin/plugin.jsonassets/drizzle-schema.tsexample-template.txtreferences/example-reference.mdscripts/example-script.shtest-connection.tstemplates/drizzle-migrations-workflow.mddrizzle-queries.tsdrizzle-schema.tsneon-basic-queries.tspackage.json详细介绍
Neon 与 Vercel Serverless Postgres
状态:已可用于生产环境
最后更新:2026-01-21
依赖项:无
最新版本:@neondatabase/[email protected]、@vercel/[email protected]、[email protected]、[email protected]、[email protected]
快速开始(5 分钟)
1. 选择平台
选项 A:Neon Direct(多云、Cloudflare Workers、任何无服务器环境)
npm install @neondatabase/serverless
选项 B:Vercel Postgres(仅限 Vercel,在 Vercel 上零配置)
npm install @vercel/postgres
注意:两者使用相同的 Neon 后端。Vercel Postgres 是配置了 Vercel 专属环境的 Neon。
这很重要的原因:
- Neon Direct 提供多云灵活性,并可访问分支 API
- Vercel Postgres 在 Vercel 上提供零配置,并自动设置环境变量
- 两者都基于 HTTP(无需 TCP),非常适合无服务器/边缘环境
2. 获取连接字符串
Neon Direct:
# Sign up at https://neon.tech
# Create a project → Get connection string
# Format: postgresql://user:[email protected]/dbname?sslmode=require
Vercel Postgres:
# In your Vercel project
vercel postgres create
vercel env pull .env.local # Automatically creates POSTGRES_URL and other vars
重要:
- 在无服务器环境中使用池化连接字符串(以
-pooler.region.aws.neon.tech结尾) - 非池化连接在无服务器环境中会很快耗尽
- 始终包含
?sslmode=require参数
3. 查询数据库
Neon Direct(Cloudflare Workers、Vercel Edge、Node.js):
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
// Simple query
const users = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Transactions
const result = await sql.transaction([
sql`INSERT INTO users (name) VALUES (${name})`,
sql`SELECT * FROM users WHERE name = ${name}`
]);
Vercel Postgres(Next.js Server Actions、API Routes):
import { sql } from '@vercel/postgres';
// Simple query
const { rows } = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Transactions
const client = await sql.connect();
try {
await client.sql`BEGIN`;
await client.sql`INSERT INTO users (name) VALUES (${name})`;
await client.sql`COMMIT`;
} finally {
client.release();
}
重要:
- 使用模板标签语法(
sql`...`)自动防止 SQL 注入 - 切勿拼接字符串:
sql('SELECT * FROM users WHERE id = ' + id)❌ - 模板标签会自动转义值并防止 SQL 注入
7 步设置流程
第 1 步:安装软件包
根据部署平台选择:
Neon Direct(Cloudflare Workers、多云、直接访问 Neon):
npm install @neondatabase/serverless
Vercel Postgres(Vercel 专属、零配置):
npm install @vercel/postgres
使用 ORM:
# Drizzle ORM (recommended for edge compatibility)
npm install [email protected] @neondatabase/[email protected]
npm install -D [email protected]
# Prisma (Node.js only)
npm install prisma @prisma/client @prisma/adapter-neon @neondatabase/serverless
要点:
- 两个软件包都使用 HTTP/WebSocket(无需 TCP)
- 兼容边缘运行环境(可在 Cloudflare Workers、Vercel Edge Runtime 中运行)
- 使用池化连接字符串时,内置连接池功能
- 无需单独的连接池库
第 2 步:创建 Neon 数据库
选项 A:Neon Dashboard
- 在 https://neon.tech 注册
- 创建新项目
- 复制池化连接字符串(重要!)
- 格式:
postgresql://user:[email protected]/db?sslmode=require
选项 B:Vercel Dashboard
- 前往你的 Vercel 项目 → Storage → Create Database → Postgres
- Vercel 会自动创建 Neon 数据库
- 运行
vercel env pull,在本地获取环境变量
选项 C:Neon CLI([email protected])
# Install CLI
npm install -g [email protected]
# Authenticate
neonctl auth
# Create project and get connection string
neonctl projects create --name my-app
neonctl connection-string main
重要:
- 始终使用池化连接字符串(以
-pooler.region.aws.neon.tech结尾) - 非池化连接用于直接连接(不适用于无服务器环境)
- 在连接字符串中包含
?sslmode=require
第 3 步:配置环境变量
Neon Direct:
# .env or .env.local
DATABASE_URL="postgresql://user:[email protected]/neondb?sslmode=require"
Vercel Postgres:
# Automatically created by `vercel env pull`
POSTGRES_URL="..." # Pooled connection (use this for queries)
POSTGRES_PRISMA_URL="..." # For Prisma migrations
POSTGRES_URL_NON_POOLING="..." # Direct connection (avoid in serverless)
POSTGRES_USER="..."
POSTGRES_HOST="..."
POSTGRES_PASSWORD="..."
POSTGRES_DATABASE="..."
Cloudflare Workers(wrangler.jsonc):
{
"vars": {
"DATABASE_URL": "postgresql://user:[email protected]/neondb?sslmode=require"
}
}
要点:
- 查询时使用
POSTGRES_URL(池化) - Prisma 迁移时使用
POSTGRES_PRISMA_URL - 切勿在无服务器函数中使用
POSTGRES_URL_NON_POOLING - 安全存储密钥(Vercel env、Cloudflare secrets 等)
第 4 步:创建数据库架构
选项 A:原始 SQL
// scripts/migrate.ts
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
await sql`
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
)
`;
选项 B:Drizzle ORM(推荐)
// db/schema.ts
import { pgTable, serial, text, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: serial('id').primaryKey(),
name: text('name').notNull(),
email: text('email').notNull().unique(),
createdAt: timestamp('created_at').defaultNow()
});
// db/index.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
# Run migrations
npx drizzle-kit generate
npx drizzle-kit migrate
选项 C:Prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("POSTGRES_PRISMA_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
createdAt DateTime @default(now()) @map("created_at")
@@map("users")
}
npx prisma migrate dev --name init
重要:
- 使用 Drizzle 作为兼容边缘运行环境的 ORM(可在 Cloudflare Workers 中运行)
- Prisma 需要 Node.js 运行时(无法在 Cloudflare Workers 中运行)
- 在 Node.js 环境中运行迁移,而非边缘函数
步骤 5:查询模式
关键——必须使用模板标签语法:
// ✅ Correct: Template tag syntax (prevents SQL injection)
const users = await sql`SELECT * FROM users WHERE email = ${email}`;
// ❌ Wrong: String concatenation (SQL injection risk)
const users = await sql('SELECT * FROM users WHERE email = ' + email);
Neon 事务 API(独有功能):
// Automatic transaction (array of queries)
const results = await sql.transaction([
sql`INSERT INTO users (name) VALUES (${name})`,
sql`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${accountId}`
]);
// Manual transaction with callback (for complex logic)
const result = await sql.transaction(async (sql) => {
const [user] = await sql`INSERT INTO users (name) VALUES (${name}) RETURNING id`;
await sql`INSERT INTO profiles (user_id) VALUES (${user.id})`;
return user;
});
Vercel Postgres 事务:
- 必须使用
sql.connect()+ 手动执行BEGIN/COMMIT/ROLLBACK - 务必在
finally块中调用client.release()(防止连接泄漏)
Drizzle 事务:
await db.transaction(async (tx) => {
await tx.insert(users).values({ name, email });
await tx.insert(profiles).values({ userId: user.id });
});
步骤 6:处理连接池
连接字符串格式:
Pooled (serverless): postgresql://user:[email protected]/db
Non-pooled (direct): postgresql://user:[email protected]/db
适用场景:
- 使用连接池(
-pooler.):无服务器函数、边缘函数、高并发场景 - 不使用连接池:长时间运行的服务、迁移、管理任务,以及无需担心连接数限制的场景
自动连接池(Neon/Vercel):
// Both packages handle pooling automatically when using pooled connection string
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!); // Pooling is automatic
连接限制:
- Neon 免费层级:100 个并发连接
- 连接池连接:在多个请求间共享连接
- 非连接池连接:每个请求都会建立新连接(很快耗尽)
关键:
- 在无服务器环境中始终使用连接池连接字符串
- 非连接池连接会导致“连接池已耗尽”错误
- 在 Neon 控制面板中监控连接使用情况
步骤 7:部署和测试
Cloudflare Workers:
// src/index.ts
import { neon } from '@neondatabase/serverless';
export default {
async fetch(request: Request, env: Env) {
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`;
return Response.json(users);
}
};
# Deploy
npx wrangler deploy
Vercel(Next.js API 路由):
// app/api/users/route.ts
import { sql } from '@vercel/postgres';
export async function GET() {
const { rows } = await sql`SELECT * FROM users`;
return Response.json(rows);
}
# Deploy
vercel deploy --prod
测试查询:
# Local test
curl http://localhost:8787/api/users
# Production test
curl https://your-app.workers.dev/api/users
要点:
- 部署前先在本地测试
- 在 Neon 控制面板中监控查询性能
- 设置连接池耗尽警报
- 使用 Neon 的查询历史记录进行调试
关键规则(Neon/Vercel 专用)
✅ 必须做到:
- 在无服务器环境中使用连接池连接字符串(主机名中包含
-pooler.) - 在连接字符串中包含
?sslmode=require - 使用模板标签语法(
sql`...`)防止 SQL 注入 - 在
finally块中调用client.release()(仅适用于 Vercel Postgres 事务) - 在 Cloudflare Workers 中使用 Drizzle(Prisma 需要 Node.js 运行时)
- 查询使用
POSTGRES_URL,Prisma 迁移使用POSTGRES_PRISMA_URL
❌ 绝不要:
- 在无服务器环境中使用非连接池连接或
POSTGRES_URL_NON_POOLING - 拼接 SQL 字符串(只能使用模板标签)
- 省略
sslmode=require(连接会失败) - 在 Cloudflare Workers 中使用 Prisma(V8 isolates 不支持它)
- 从边缘函数运行迁移(使用 Node.js 环境)
防止已知问题
此技能可防止 19 个已记录的问题:
问题 #1:连接池耗尽
错误:Error: connection pool exhausted 或 too many connections for role
来源:https://github.com/neondatabase/serverless/issues/12
原因:在高并发无服务器环境中使用非连接池连接字符串
预防措施:始终使用连接池连接字符串(主机名中包含 -pooler.)。检查连接字符串格式。
问题 #2:不支持 TCP 连接
错误:Error: TCP connections are not supported in this environment
来源:Cloudflare Workers 文档
原因:传统 Postgres 客户端使用 TCP 套接字,而边缘运行时无法使用 TCP 套接字
预防措施:使用基于 HTTP/WebSocket 的 @neondatabase/serverless,而不是 pg 或 postgres.js 软件包。
问题 #3:字符串拼接导致 SQL 注入
错误:SQL 注入攻击成功或查询结果异常
来源:OWASP SQL 注入指南
原因:将用户输入拼接到 SQL 字符串中:sql('SELECT * FROM users WHERE id = ' + id)
预防措施:始终使用模板标签语法:sql`SELECT * FROM users WHERE id = ${id}`。模板标签会自动转义值。
问题 #4:缺少 SSL 模式
错误:Error: connection requires SSL 或 FATAL: no pg_hba.conf entry
来源:https://neon.tech/docs/connect/connect-securely
原因:连接字符串中缺少 ?sslmode=require 参数
预防措施:始终在连接字符串后追加 ?sslmode=require。
问题 #5:连接泄漏(Vercel Postgres)
错误:内存使用量逐渐增加,最终出现超时错误
来源:https://github.com/vercel/storage/issues/45
原因:手动执行事务后忘记调用 client.release()
预防:始终使用 try/finally 代码块,并在 finally 代码块中调用 client.release()。
问题 #6:环境变量错误(Vercel)
错误:Error: Connection string is undefined 或 connect ECONNREFUSED
来源:https://vercel.com/docs/storage/vercel-postgres/using-an-orm
原因:使用了 DATABASE_URL 而不是 POSTGRES_URL,或反之
预防:查询使用 POSTGRES_URL,Prisma 迁移使用 POSTGRES_PRISMA_URL。
问题 #7:边缘函数中的事务超时
错误:Error: Query timeout 或 Error: transaction timeout
来源:https://neon.tech/docs/introduction/limits
原因:长时间运行的事务超出边缘函数的超时时间(通常为 30s)
预防:缩短事务耗时(<5s)、批量执行操作,或将复杂事务移至后台工作进程。
问题 #8:Cloudflare Workers 中的 Prisma
错误:Error: PrismaClient is unable to be run in the browser 或模块解析错误
来源:https://github.com/prisma/prisma/issues/18765
原因:Prisma 需要支持文件系统访问的 Node.js 运行时
预防:在 Cloudflare Workers 中使用 Drizzle ORM。Prisma 仅适用于 Vercel Edge/Node.js 运行时。
问题 #9:分支 API 身份验证错误
错误:调用 Neon API 时出现 Error: Unauthorized
来源:https://neon.tech/docs/api/authentication
原因:缺少 NEON_API_KEY 环境变量,或该变量无效
预防:在 Neon 控制台中依次进入 Account Settings → API Keys,创建 API 密钥并将其设为环境变量。
问题 #10:删除分支后连接过时
错误:删除分支后出现 Error: database "xyz" does not exist
来源:https://neon.tech/docs/guides/branching
原因:应用仍在使用已删除分支的连接字符串
预防:切换分支时更新 DATABASE_URL,并在分支变更后重启应用。
问题 #11:冷启动/自动挂起时查询超时
错误:空闲一段时间后首次请求出现 Error: Query timeout,或出现 Connection terminated unexpectedly
来源:Neon 文档 - 自动挂起 | GitHub 问题 #168 | 2025 年 12 月更新日志
原因:Neon 在空闲约 5 分钟后会自动挂起计算资源(免费套餐),导致约 1–2s 的唤醒延迟或连接终止
预防:
- 将查询超时时间设为 >= 10s,以应对冷启动
- 使用 HTTP 客户端(
neon()),它可以透明地处理自动挂起 - 使用 Pool 处理连接终止错误:
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// CRITICAL: Handle connection termination errors
pool.on('error', (err) => {
console.error('Unexpected database error:', err);
// Implement reconnection logic or alerting
});
- 生产环境配置:禁用自动挂起,以确保性能稳定
- 在 Neon 控制台中:将最小计算单元设为 > 0
- 或使用 >= 16 CU 的计算规格(自动禁用缩至零)
- 权衡:需为闲置时间付费,但能获得稳定的 <100ms 查询
- 即使禁用了自动挂起,也应使用池化连接字符串以获得最佳性能
问题 #12:Drizzle 架构不匹配
错误:出现 Property 'x' does not exist on type 'User' 等 TypeScript 错误
来源:https://orm.drizzle.team/docs/generate
原因:数据库架构已变更,但 Drizzle 类型未重新生成
预防:架构变更后运行 npx drizzle-kit generate,并提交生成的文件。
问题 #13:不同分支之间的迁移冲突
错误:Error: relation "xyz" already exists 或迁移版本冲突
来源:https://neon.tech/docs/guides/branching#schema-migrations
发生原因:多个分支具有不同的迁移历史
预防方法:在主分支上运行迁移后再创建分支,或在合并前重置分支架构。
问题 #14:PITR 时间戳超出范围
错误:Error: timestamp is outside retention window
来源:https://neon.tech/docs/introduction/point-in-time-restore
发生原因:尝试从早于保留期限的时间戳恢复(免费套餐为 7 天)
预防方法:查看套餐的保留期限,并在允许的时间范围内恢复。
问题 #15:Prisma 使用了错误的适配器
错误:Error: Invalid connection string 或查询性能缓慢
来源:https://www.prisma.io/docs/orm/overview/databases/neon
发生原因:在无服务器环境中未使用 @prisma/adapter-neon
预防方法:安装 @prisma/adapter-neon 和 @neondatabase/serverless,并配置 Prisma 使用基于 HTTP 的连接。
问题 #16:边缘运行时需要 poolQueryViaFetch
错误:WebSocket is not defined 或使用 use cache 时 Next.js 15 预渲染超时
来源:Neon 文档 - Prisma 指南 | GitHub 问题 #181
发生原因:Cloudflare Workers 等边缘运行时在执行 Pool 查询时需要使用 HTTP,而不是 WebSocket。使用 poolQueryViaFetch 时,Next.js 15 的 use cache 指令可能会导致超时。
预防方法:在边缘环境中使用 Pool 之前设置 neonConfig.poolQueryViaFetch = true。
import { Pool, neonConfig } from '@neondatabase/serverless';
// Enable Pool queries over HTTP fetch (required for edge)
neonConfig.poolQueryViaFetch = true;
const pool = new Pool({ connectionString: env.DATABASE_URL });
export default {
async fetch(request: Request, env: Env) {
// Pool.query() now uses HTTP instead of WebSocket
const result = await pool.query('SELECT * FROM users');
return Response.json(result.rows);
}
};
注意事项 - Next.js 15 use cache:避免将 poolQueryViaFetch = true 与 use cache 指令搭配使用,改用 neon() HTTP 客户端:
// ❌ Can timeout during prerender
import { Pool, neonConfig } from '@neondatabase/serverless';
neonConfig.poolQueryViaFetch = true;
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
async function getData() {
'use cache';
return await pool.query('SELECT * FROM data');
}
// ✅ Works with prerender
import { neon } from '@neondatabase/serverless';
const sql = neon(process.env.DATABASE_URL!);
async function getData() {
'use cache';
return await sql`SELECT * FROM data`;
}
问题 #17:Node v20 并行操作导致事务上下文丢失
错误:在事务中使用 Promise.all() 时违反外键约束:insert or update on table violates foreign key constraint
来源:Drizzle 问题 #2200
发生原因:在 Node.js v20+ 中使用 Neon 无服务器驱动和 Drizzle ORM 时,通过 Promise.all() 在事务中并行执行数据库操作会导致事务上下文丢失。顺序执行操作则可以正常工作。这是 Neon 驱动在 Node v20 中处理会话时特有的事务上下文管理问题。
预防方法:改用顺序执行操作,或在 Node.js 环境中切换到 postgres-js 驱动(不兼容边缘运行时)。
// ❌ FAILS in Node v20 with Neon driver
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ name: 'Alice' }).returning();
// Parallel inserts lose transaction context
await Promise.all([
tx.insert(userSettings).values({ userId: user.id, theme: 'dark' }),
tx.insert(userSettings).values({ userId: user.id, locale: 'en' })
]);
// Error: Foreign key constraint violation (user.id not visible)
});
// ✅ WORKS - Sequential execution
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ name: 'Alice' }).returning();
await tx.insert(userSettings).values({ userId: user.id, theme: 'dark' });
await tx.insert(userSettings).values({ userId: user.id, locale: 'en' });
});
// ✅ ALTERNATIVE - Use postgres-js driver for Node.js (not edge-compatible)
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
const client = postgres(connectionString);
const db = drizzle(client);
// Promise.all() works correctly with this driver
受影响的配置:
- Node.js:v20.12.2+
- @neondatabase/serverless:v0.9.0+(已确认至 v1.0.x)
- drizzle-orm:v0.30.8+
- 模式:在事务中使用
Promise.all()并行插入
问题 #18:沙盒运行时中访问 process.env
错误:ReferenceError: process is not defined
来源:GitHub 问题 #179
发生原因:Neon 无服务器驱动会在顶层无条件访问 process.env.*,这会导致 Slack 的 Deno 运行时等未提供 process.env 的沙盒运行时报错。
受影响的环境:Slack Deno 运行时、没有 Node.js process 全局对象的沙盒 JavaScript 环境
预防方法:目前没有可用的解决方法。用户必须:
- 在运行时中为
process.env添加 polyfill - 使用其他 Postgres 驱动(兼容 Deno 的标准
pg) - 等待
@neondatabase/serverless和底层 pg(同样会访问 process.env)修复此问题
官方状态:问题仍未解决,目前没有提供修复时间表。Neon 驱动和底层 pg 库均受影响。
从 v0.x 迁移到 v1.0+
破坏性变更:v1.0.0 要求所有 SQL 查询都使用带标签的模板语法。 来源:Neon 博客文章 | GitHub Issue #3678
之前(v0.x):
const result = await sql("SELECT * FROM users WHERE id = $1", [userId]);
之后(v1.0+):
// Option 1: Tagged template (recommended)
const result = await sql`SELECT * FROM users WHERE id = ${userId}`;
// Option 2: .query() method for parameterized queries
const result = await sql.query("SELECT * FROM users WHERE id = $1", [userId]);
// Option 3: .unsafe() for trusted raw SQL (dynamic identifiers)
const column = 'name';
const result = await sql`SELECT ${sql.unsafe(column)} FROM users`;
变更原因:v1.0.0 版本强制使用带标签的模板语法,以防止 SQL 注入漏洞。函数调用语法 sql("...", [params]) 现在会抛出运行时错误。
错误消息:
This function can now be called only as a tagged-template function:
sql`SELECT ${value}`, not sql("SELECT $1", [value], options)
迁移检查清单:
- 将所有
sql("...", [params])调用替换为带标签的模板 - 如果将 better-auth 与 Drizzle 搭配使用,请将 drizzle-orm 升级到 v0.40.1+(解决不兼容问题)
- 使用新语法测试所有动态查询
- 检查 SQL 注入防护模式(模板标签会自动转义)
better-auth 用户:如果将 better-auth v1.3.4+ 与 Neon v1.0.0+ 搭配使用,请将 drizzle-orm 升级到 v0.40.1 或更高版本,以解决兼容性问题:
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"better-auth": "^1.3.4",
"drizzle-orm": "^0.40.1"
}
}
替代解决方法:在 better-auth 中使用 Kysely 替代 Drizzle(无需更新 drizzle-orm 即可使用):
import { Kysely } from 'kysely';
import { Pool } from '@neondatabase/serverless';
const db = new Kysely({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL })
})
});
性能与协议选择
来源:Neon 博客 - HTTP 与 WebSockets
HTTP 和 WebSocket 协议的性能特征差异显著。协议选择会影响延迟、吞吐量以及可用的 Postgres 功能。
性能基准
- HTTP 单次查询:初始延迟约为 37ms
- WebSocket 初始连接:开销约为 15-20ms
- WebSocket 后续查询:每次查询约为 4-5ms
- 盈亏平衡点:顺序执行 2-3 次查询(此时 WebSocket 更快)
协议决策矩阵
| 用例 | 推荐方案 | 原因 |
|---|---|---|
| 每个请求执行单次查询 | HTTP(neon()) | 初始延迟较低(约 37ms) |
| 顺序执行 2 次或更多查询 | WebSocket(Pool/Client) | 每次查询的延迟较低(约 5ms) |
| 并行执行独立查询 | HTTP | 并行处理能力更强 |
| 交互式事务 | WebSocket(必需) | 事务上下文需要此协议 |
| Edge Functions(单次执行) | HTTP | 无连接开销 |
| 长时间运行的工作进程 | WebSocket | 分摊连接成本 |
代码示例
// HTTP: Best for single queries
import { neon } from '@neondatabase/serverless';
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`; // ~37ms
// WebSocket: Best for multiple sequential queries
import { Pool } from '@neondatabase/serverless';
const pool = new Pool({ connectionString: env.DATABASE_URL });
const client = await pool.connect(); // ~15ms setup
try {
const user = await client.query('SELECT * FROM users WHERE id = $1', [1]); // ~5ms
const posts = await client.query('SELECT * FROM posts WHERE user_id = $1', [1]); // ~5ms
const comments = await client.query('SELECT * FROM comments WHERE user_id = $1', [1]); // ~5ms
// Total: ~30ms (vs ~111ms with HTTP)
} finally {
client.release();
}
重要限制
HTTP 不支持:
- 交互式事务(BEGIN/COMMIT/ROLLBACK)
- 会话级功能(临时表、预处理语句)
- LISTEN/NOTIFY
- COPY 协议
Edge 环境中的 WebSocket 限制:
- 无法在多个请求之间保持连接
- 必须在单个请求处理程序中完成连接、使用和关闭
配置文件参考
package.json(Neon 直连)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2"
}
}
package.json(Vercel Postgres)
{
"dependencies": {
"@vercel/postgres": "^0.10.0"
}
}
package.json(搭配 Drizzle ORM)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"drizzle-orm": "^0.44.7"
},
"devDependencies": {
"drizzle-kit": "^0.31.7"
},
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
}
}
drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './db/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!
}
});
这些设置的原因:
@neondatabase/serverless兼容 Edge(基于 HTTP/WebSocket)@vercel/postgres在 Vercel 上提供零配置体验drizzle-orm可在所有运行时中使用(Cloudflare Workers、Vercel Edge、Node.js)drizzle-kit负责处理迁移和架构生成
常见模式
模式 1:使用 Neon 的 Cloudflare Worker
import { neon } from '@neondatabase/serverless';
interface Env { DATABASE_URL: string; }
export default {
async fetch(request: Request, env: Env) {
const sql = neon(env.DATABASE_URL);
const users = await sql`SELECT * FROM users`;
return Response.json(users);
}
};
模式 2:使用 Next.js 的 Vercel Postgres
'use server';
import { sql } from '@vercel/postgres';
export async function getUsers() {
const { rows } = await sql`SELECT * FROM users`;
return rows;
}
模式 3:Drizzle ORM 设置
// db/index.ts
import { drizzle } from 'drizzle-orm/neon-http';
import { neon } from '@neondatabase/serverless';
import * as schema from './schema';
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
// Usage: Type-safe queries with JOINs
const postsWithAuthors = await db
.select({ postId: posts.id, authorName: users.name })
.from(posts)
.leftJoin(users, eq(posts.userId, users.id));
模式 4:Neon 自动事务
有关 Neon 独特的事务 API(数组语法或回调语法),请参阅步骤 5。
模式 5:使用 Neon 分支创建预览环境
# Create branch for PR
neonctl branches create --project-id my-project --name pr-123 --parent main
# Get connection string for branch
BRANCH_URL=$(neonctl connection-string pr-123)
# Use in Vercel preview deployment
vercel env add DATABASE_URL preview
# Paste $BRANCH_URL
# Delete branch when PR is merged
neonctl branches delete pr-123
# .github/workflows/preview.yml
name: Create Preview Database
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Create Neon Branch
run: |
BRANCH_NAME="pr-${{ github.event.pull_request.number }}"
neonctl branches create --project-id ${{ secrets.NEON_PROJECT_ID }} --name $BRANCH_NAME
BRANCH_URL=$(neonctl connection-string $BRANCH_NAME)
- name: Deploy to Vercel
env:
DATABASE_URL: ${{ steps.branch.outputs.url }}
run: vercel deploy --env DATABASE_URL=$DATABASE_URL
适用场景:希望为每个 PR/预览部署使用隔离的数据库
使用捆绑资源
脚本(scripts/)
setup-neon.sh - 创建 Neon 数据库并输出连接字符串
chmod +x scripts/setup-neon.sh
./scripts/setup-neon.sh my-project-name
test-connection.ts - 验证数据库连接并运行测试查询
npx tsx scripts/test-connection.ts
参考资料(references/)
references/connection-strings.md- 连接字符串格式的完整指南,以及连接池与非连接池的区别references/drizzle-setup.md- 使用 Neon 配置 Drizzle ORM 的分步指南references/prisma-setup.md- 使用 Neon adapter 配置 Prismareferences/branching-guide.md- Neon 数据库分支的综合指南references/migration-strategies.md- 适用于不同 ORM 和工具的迁移模式references/common-errors.md- 扩展版故障排查指南
Claude 应在以下情况加载这些文件:
- 调试连接问题时,加载
connection-strings.md - 用户想使用 Drizzle ORM 时,加载
drizzle-setup.md - 用户想使用 Prisma 时,加载
prisma-setup.md - 用户询问预览环境或数据库分支时,加载
branching-guide.md - 遇到特定错误消息时,加载
common-errors.md
资源文件(assets/)
assets/schema-example.sql- 包含用户、帖子和评论的数据库架构示例assets/drizzle-schema.ts- 完整的 Drizzle 架构模板assets/prisma-schema.prisma- 完整的 Prisma 架构模板
高级主题
数据库分支(Neon 特有功能)
Neon 提供类似 Git 的数据库分支:
# Create branch from main
neonctl branches create --name dev --parent main
# Create from point-in-time (PITR restore)
neonctl branches create --name restore --parent main --timestamp "2025-10-28T10:00:00Z"
# Get connection string for branch
neonctl connection-string dev
# Delete branch
neonctl branches delete feature
主要功能:
- 写时复制:分支创建即时完成(不复制数据)
- 预览部署:每个 PR 创建一个分支,合并时删除
- 时间点恢复:恢复到特定时间戳(免费套餐保留 7 天)
- 计算资源共享:分支共享计算资源限额(免费套餐),或使用独立计算资源(付费套餐)
性能与安全说明
连接池监控:
- 在 Neon 控制面板中查看用量(连接数限制:免费套餐为 100,使用连接池时约为 10,000)
- 使用量超过 80% 时设置提醒
- 使用连接池连接字符串,避免出现“connection pool exhausted”错误
查询优化:
- 为经常查询的列创建索引
- 避免 N+1 查询(使用 JOIN 或 Drizzle relations)
- 对重复查询使用 Drizzle 预处理语句
安全:
- 切勿将连接字符串硬编码(使用环境变量)
- 模板标签语法可防止 SQL 注入
- 多租户应用使用行级安全(RLS)
- 查询前使用 Zod 验证输入
依赖项
必需:
@neondatabase/serverless@^1.0.2- Neon 无服务器 Postgres 客户端(基于 HTTP/WebSocket)@vercel/postgres@^0.10.0- Vercel Postgres 客户端(Neon 直连的替代方案,专用于 Vercel)
可选:
drizzle-orm@^0.44.7- TypeScript ORM(兼容边缘环境,推荐)drizzle-kit@^0.31.7- Drizzle 架构迁移和内省工具@prisma/client@^6.10.0- Prisma ORM(仅限 Node.js,不兼容边缘环境)@prisma/adapter-neon@^6.10.0- 用于 Neon 无服务器的 Prisma adapterneonctl@^2.19.0- 用于数据库管理的 Neon CLIzod@^3.24.0- 用于输入清理的架构验证工具
官方文档
- Neon 文档:https://neon.tech/docs
- Neon Serverless 软件包:https://github.com/neondatabase/serverless
- Vercel Postgres:https://vercel.com/docs/storage/vercel-postgres
- Vercel Storage(全部):https://vercel.com/docs/storage
- Neon 分支指南:https://neon.tech/docs/guides/branching
- Neonctl CLI:https://neon.tech/docs/reference/cli
- Drizzle + Neon:https://orm.drizzle.team/docs/quick-postgresql/neon
- Prisma + Neon:https://www.prisma.io/docs/orm/overview/databases/neon
- Context7 库 ID:
/github/neondatabase/serverless、/github/vercel/storage
软件包版本(已于 2026-01-09 验证)
{
"dependencies": {
"@neondatabase/serverless": "^1.0.2",
"@vercel/postgres": "^0.10.0",
"drizzle-orm": "^0.45.1"
},
"devDependencies": {
"drizzle-kit": "^0.31.8",
"neonctl": "^2.19.0"
}
}
最新 Prisma(如需):
{
"dependencies": {
"@prisma/client": "^6.10.0",
"@prisma/adapter-neon": "^6.10.0"
},
"devDependencies": {
"prisma": "^6.10.0"
}
}
生产环境示例
此技能基于 Neon 和 Vercel Postgres 的生产部署:
- Cloudflare Workers:API 每日处理超过 50K 个请求,连接错误数为 0
- Vercel Next.js App:电商网站月活用户超过 100K
- 构建时间:<5 分钟(初始设置),<30s(部署)
- 错误:0(预防了全部 19 个已知问题)
- 验证:✅ 连接池,✅ SQL 注入防护,✅ 事务处理,✅ 分支工作流,✅ Edge 运行时兼容性,✅ Node v20 事务模式
故障排除
问题:Error: connection pool exhausted
解决方案:
- 确认使用的是池化连接字符串(以
-pooler.region.aws.neon.tech结尾) - 在 Neon 控制面板中检查连接使用情况
- 如果持续达到限制,请升级到更高等级
- 优化查询以缩短连接占用时间
问题:Error: TCP connections are not supported
解决方案:
- 使用
@neondatabase/serverless,而不是pg或postgres.js - 确认没有导入传统的 Postgres 客户端
- 检查构建包是否包含基于 HTTP/WebSocket 的客户端
问题:Error: database "xyz" does not exist
解决方案:
- 确认
DATABASE_URL指向正确的数据库 - 如果使用 Neon 分支,请确保分支仍然存在
- 检查连接字符串格式(确保没有拼写错误)
问题:冷启动时查询缓慢
解决方案:
- Neon 会在闲置 5 分钟后自动暂停(免费等级)
- 唤醒后的首次查询需要约 1-2 秒
- 将查询超时时间设为 >= 10s,以应对冷启动
- 付费套餐可关闭自动暂停,让数据库始终运行
问题:PrismaClient is unable to be run in the browser
解决方案:
- Prisma 无法在 Cloudflare Workers(V8 isolates)中运行
- 使用 Drizzle ORM 作为兼容 Edge 的 ORM
- Prisma 可在 Vercel Edge/Node.js 运行时中配合
@prisma/adapter-neon使用
问题:不同分支之间的迁移版本冲突
解决方案:
- 先在 main 分支上运行迁移
- 再创建功能分支
- 或在合并前重置分支架构:
neonctl branches reset feature --parent main
问题:使用 drizzle-kit 时出现“WebSocket warning”(社区提供)
错误:Warning: @neondatabase/serverless can only connect to remote Neon/Vercel Postgres/Supabase instances through a websocket
来源:GitHub Discussion #12508
解决方案:此警告仅用于提示,可以放心忽略。尽管出现警告,迁移仍能正常工作。此警告用于告知用户正在使用 WebSocket 协议,如果出现问题,可帮助调试。添加 pg 作为开发依赖项可以消除警告,但没有必要。
问题:VPN 阻止 Neon 连接(社区提供)
错误:NeonDbError: Error connecting to database: fetch failed [cause]: SocketError: other side closed
来源:GitHub Issue #146 comment
解决方案:某些 VPN 会阻止与 Neon 端点之间的 WebSocket 或 fetch 连接。此问题主要发生在使用 Next.js 14+ 服务端操作进行开发(localhost)时。请关闭 VPN 并进行测试,或在 VPN 配置中将 Neon 域名加入白名单:
# Whitelist these domains in VPN:
*.neon.tech
*.aws.neon.tech
完整设置检查清单
使用此清单验证你的设置:
- 已安装软件包(
@neondatabase/serverless或@vercel/postgres) - 已创建 Neon 数据库(或已配置 Vercel Postgres)
- 已获取池化连接字符串(以
-pooler.结尾) - 连接字符串包含
?sslmode=require - 已配置环境变量(
DATABASE_URL或POSTGRES_URL) - 已创建数据库架构(原始 SQL、Drizzle 或 Prisma)
- 查询使用模板标签语法(
sql`...`) - 事务使用正确的 try/catch 并释放连接
- 已验证连接池(使用池化连接字符串)
- ORM 选择适合运行时(边缘环境使用 Drizzle,Node.js 使用 Prisma)
- 已使用开发数据库在本地进行测试
- 已在生产/预览环境中部署并测试
- 已在 Neon 控制面板中设置连接监控
有问题或遇到故障?
- 查看
references/common-errors.md,获取更详细的故障排除信息 - 验证 7 步设置流程中的所有步骤
- 查看官方文档:https://neon.tech/docs
- 确保在无服务器环境中使用池化连接字符串
- 验证连接字符串中包含
sslmode=require - 使用
scripts/test-connection.ts测试连接
最后验证日期:2026-01-21 | 技能版本:2.0.0 | 变更:新增 4 个问题(#16-#19:poolQueryViaFetch、Node v20 事务、process.env 沙盒化);新增迁移指南(v0.x→v1.0+);新增性能与协议选择部分;扩展问题 #11,加入自动挂起处理和生产环境配置;新增第 2 级社区来源的故障排除内容(WebSocket 警告、VPN 阻止)