mcpField
N
neon-vercel-postgres

neon-vercel-postgres

github.com/jezweb数据与数据库1.1k363 次安装3 周前更新
代码仓库

关于 neon-vercel-postgres

面向在无服务器和边缘环境部署应用的开发者,介绍如何使用 Neon 或 Vercel Postgres。内容涵盖配置、安全查询、连接池、模式管理及部署注意事项。

主要功能

  1. 01Neon 与 Vercel 配置指南
  2. 02无服务器连接池
  3. 03SQL 模板标签查询方式
  4. 04Drizzle 与 Prisma 模式工作流
  5. 05事务和部署指南

适用场景

  1. 01为 Cloudflare Workers 连接 Postgres
  2. 02在 Vercel 构建 Next.js 应用
  3. 03避免无服务器环境连接耗尽
  4. 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.json
assets/
drizzle-schema.ts
example-template.txt
references/
example-reference.md
scripts/
example-script.sh
test-connection.ts
templates/
drizzle-migrations-workflow.md
drizzle-queries.ts
drizzle-schema.ts
neon-basic-queries.ts
package.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

  1. 在 https://neon.tech 注册
  2. 创建新项目
  3. 复制池化连接字符串(重要!)
  4. 格式:postgresql://user:[email protected]/db?sslmode=require

选项 B:Vercel Dashboard

  1. 前往你的 Vercel 项目 → Storage → Create Database → Postgres
  2. Vercel 会自动创建 Neon 数据库
  3. 运行 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 环境 预防方法:目前没有可用的解决方法。用户必须:

  1. 在运行时中为 process.env 添加 polyfill
  2. 使用其他 Postgres 驱动(兼容 Deno 的标准 pg)
  3. 等待 @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 配置 Prisma
  • references/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 adapter
  • neonctl@^2.19.0 - 用于数据库管理的 Neon CLI
  • zod@^3.24.0 - 用于输入清理的架构验证工具

官方文档


软件包版本(已于 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

解决方案:

  1. 确认使用的是池化连接字符串(以 -pooler.region.aws.neon.tech 结尾)
  2. 在 Neon 控制面板中检查连接使用情况
  3. 如果持续达到限制,请升级到更高等级
  4. 优化查询以缩短连接占用时间
问题: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 控制面板中设置连接监控

有问题或遇到故障?

  1. 查看 references/common-errors.md,获取更详细的故障排除信息
  2. 验证 7 步设置流程中的所有步骤
  3. 查看官方文档:https://neon.tech/docs
  4. 确保在无服务器环境中使用池化连接字符串
  5. 验证连接字符串中包含 sslmode=require
  6. 使用 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 阻止)

评论

支持 Markdown