关于 api-baas-neon
面向使用 Neon 无服务器 PostgreSQL 驱动的开发者,介绍边缘与无服务器场景下的查询、连接及分支管理,并涵盖 SQL 安全实践。
主要功能
- 01使用 `neon()` 执行 HTTP 查询
- 02管理 WebSocket 连接池与客户端
- 03通过 HTTP 执行原子事务
- 04区分池化与直连方式
- 05创建数据库分支
- 06处理冷启动
适用场景
- 01在边缘函数中查询 PostgreSQL
- 02在无服务器处理程序中运行事务
- 03创建开发或预览分支
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-baas-neon安装后在 Claude Code 中输入/api-baas-neon确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/branching.mdcore.md详细介绍
Neon Serverless PostgreSQL 模式
快速指南: 使用
@neondatabase/serverless在边缘/无服务器环境中访问数据库。单条查询优先使用neon()HTTP 函数(速度更快且无状态);交互式事务则使用Pool/Client。无服务器工作负载使用连接池连接字符串(带-pooler后缀),仅在迁移时使用直连。利用写时复制语义为开发/预览环境创建数据库分支。务必处理缩容至零时的冷启动(唤醒需要 200-500ms)。
<critical_requirements>
重要:使用此技能前
(在边缘/无服务器运行时执行单条查询时,必须使用 neon() HTTP 函数——对于一次性操作,它比 WebSocket 快 2-3 倍)
(在无服务器环境中,必须在同一个请求处理程序内关闭 Pool/Client 连接——WebSocket 连接无法超出单个请求的生命周期)
(无服务器工作负载必须使用连接池连接字符串(带 -pooler 后缀)——直连会耗尽有限的连接槽位)
(必须通过适当的连接超时和重试逻辑,处理缩容至零的唤醒延迟(200-500ms))
(仅对表名/列名等可信且已知安全的字符串使用 sql.unsafe()——绝不要用于用户输入)
</critical_requirements>
自动检测: Neon、@neondatabase/serverless、neon()、neonConfig、neon 无服务器驱动、neon 数据库、neon 分支、neonctl、neon 连接池、neon 缩容至零、neon 自动扩缩容、neon postgres、ep-*-pooler
适用场景:
- 在边缘/无服务器函数(边缘运行时、无服务器平台)中查询 Postgres
- 为不同工作负载设置连接字符串(连接池连接与直连)
- 为开发、预览或 CI 环境创建数据库分支
- 管理缩容至零的行为并优化冷启动
- 在无服务器环境中运行事务(HTTP 批处理或 WebSocket)
- 通过 Neon API 或 neonctl CLI 以编程方式管理分支
涵盖的关键模式:
- 使用 SQL 标记模板和可组合片段执行
neon()HTTP 查询 - 正确管理生命周期的
Pool/ClientWebSocket 连接 - 连接池(
-pooler)与直连连接字符串,以及各自的适用场景 - 数据库分支(开发分支、PR 预览分支、仅含架构的分支)
- 缩容至零行为、冷启动缓解和自动扩缩容
- 用于非交互式 HTTP 事务的
sql.transaction() - 使用 Neon API 和 neonctl CLI 以编程方式管理分支
不适用场景:
- 传统的长生命周期服务器连接(使用标准
pg驱动和 TCP) - ORM 特有的复杂模式(使用对应 ORM 的技能)
- 通用 PostgreSQL 查询语法(使用 SQL/Postgres 技能)
详细资源:
- 有关决策框架和快速查询表,请参阅 reference.md
驱动与查询:
- examples/core.md -- 驱动设置、HTTP 查询、WebSocket 连接、事务
分支与运维:
- examples/branching.md -- 开发分支、PR 预览、neonctl CLI、Neon API、CI/CD 工作流
## 理念
Neon 将 PostgreSQL 的存储与计算分离,支持传统 Postgres 无法实现的无服务器功能:缩容至零、即时分支和自动扩缩容。@neondatabase/serverless 驱动将 TCP 替换为 HTTP 和 WebSockets,使 Postgres 可在不支持 TCP 的边缘运行时中使用。
核心原则:
- HTTP 追求速度,WebSocket 用于会话——
neon()函数对单条查询使用 HTTP fetch(约 3 次往返)。需要会话或交互式事务时,Pool/Client使用 WebSockets(约 8 次往返)。应根据任务选择合适的传输方式。 - 默认使用连接池——池化连接通过 PgBouncer(事务模式)路由,可处理多达 10,000 个并发客户端。直接连接受计算资源规模限制(100-4,000),仅应用于迁移或需要会话状态的功能。
- 分支成本低——采用写时复制,因此 500GB 数据库的分支在数据出现差异前无需分配额外存储。可放心将分支用于开发、预览、测试和 CI。
- 默认缩容至零——计算资源在闲置 5 分钟后暂停。冷启动需要 200-500ms。设计时应考虑超时、重试和连接池。
- 内置 SQL 注入防护——带标签的模板函数会自动参数化。自 v1.0 起,将
neon()作为普通函数调用会产生类型错误,以防意外注入。
适用场景:
- 无法建立 TCP 连接的边缘/无服务器函数
- 可从数据库分支中受益的应用(为每个 PR 创建预览环境)
- 可从缩容至零中受益的成本敏感型工作负载
- 需要连接池的高并发无服务器应用
不适用场景:
- 使用持久连接的长时间运行服务器进程(请通过 TCP 使用标准的
pg) - 需要通过 PgBouncer 使用会话级功能的工作负载(LISTEN/NOTIFY、SET、临时表)
- 需要始终开启计算资源且规模超过 16 CU 的数据库(超过 16 CU 时不提供缩容至零)
核心模式
模式 1:使用 neon() 执行 HTTP 查询
neon() 函数使用带 SQL 标签的模板创建基于 HTTP 的查询函数。这是执行单条非交互式查询的最快方式。
import { neon } from "@neondatabase/serverless";
const DATABASE_URL = process.env.DATABASE_URL!;
const sql = neon(DATABASE_URL);
// Tagged template -- parameters are auto-parameterized (safe from injection)
const userId = "abc-123";
const posts =
await sql`SELECT id, title FROM posts WHERE author_id = ${userId}`;
优点: 带标签的模板会自动参数化值,防止 SQL 注入;HTTP 约需 3 次往返,相比之下 WebSocket 约需 8 次;无状态(无需管理连接)
// BAD: Calling neon() as a regular function (v1.0+ type error)
const sql = neon(DATABASE_URL);
const result = await sql(`SELECT * FROM posts WHERE id = ${id}`); // TYPE ERROR + SQL injection risk
缺点: 自 v1.0 起,将查询函数作为普通函数(而非带标签的模板)调用会导致运行时错误和类型错误——此更改专为防止通过字符串插值导致 SQL 注入而做
用于动态 SQL 的 sql.query()
当查询字符串存储在变量中(而非模板字面量)时,请使用带编号占位符的 .query() 方法:
const q = "SELECT * FROM posts WHERE id = $1 AND status = $2";
const posts = await sql.query(q, [postId, "published"]);
适用场景: 运行时构建的动态 SQL 字符串,或存储在变量中的查询。参数仍会通过 $1、$2 等安全地进行参数化。
模式 2:可组合的查询片段
模板查询支持组合使用,并会自动为各片段中的参数编号。
import { neon } from "@neondatabase/serverless";
const sql = neon(DATABASE_URL);
// Build queries from reusable fragments
const whereClause = sql`WHERE status = ${"active"} AND role = ${"admin"}`;
const orderClause = sql`ORDER BY created_at DESC`;
const PAGE_SIZE = 20;
const limitClause = sql`LIMIT ${PAGE_SIZE}`;
const users =
await sql`SELECT id, name, email FROM users ${whereClause} ${orderClause} ${limitClause}`;
优点: 组合片段中的参数会自动重新编号;页面大小使用命名常量;片段可在多个查询中重复使用
使用 sql.unsafe() 动态指定表名/列名
// ONLY for trusted, known-safe values -- never user input
const TABLE_NAME = "posts";
const results =
await sql`SELECT * FROM ${sql.unsafe(TABLE_NAME)} WHERE id = ${postId}`;
适用场景: 仅当需要插入无法在 SQL 中参数化的可信标识符(表名、列名)时使用。切勿将用户提供的值传递给 sql.unsafe()。
### 模式 3:使用 `sql.transaction()` 执行 HTTP 事务
通过 HTTP 原子地执行多条查询,无需建立 WebSocket 连接。
import { neon } from "@neondatabase/serverless";
const sql = neon(DATABASE_URL);
// Array form -- all queries execute in a single HTTP round trip
const [posts, totalCount] = await sql.transaction([
sql`SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 10`,
sql`SELECT count(*) FROM posts`,
]);
// Function form with transaction options
const [, transferResult] = await sql.transaction(
(txn) => [
txn`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${fromId}`,
txn`UPDATE accounts SET balance = balance + ${amount} WHERE id = ${toId}`,
],
{ isolationLevel: "Serializable" },
);
优点: 无需承担 WebSocket 开销即可原子执行;数组形式适用于简洁地批量执行只读查询;函数形式支持事务选项;隔离级别可控制一致性保证
// BAD: Running related queries as separate HTTP requests
const posts = await sql`SELECT * FROM posts WHERE author_id = ${authorId}`;
const author = await sql`SELECT * FROM users WHERE id = ${authorId}`;
// Two separate HTTP round trips, not atomic, race conditions possible
缺点: 单独的 HTTP 调用不具备原子性;查询之间数据可能发生变化;网络延迟会加倍
事务选项
isolationLevel:ReadUncommitted|ReadCommitted|RepeatableRead|SerializablereadOnly:布尔值(默认值为false)deferrable:布尔值(默认值为false,仅在readOnly: true+Serializable时生效)
模式 4:使用 Pool/Client 建立 WebSocket 连接
使用 Pool 和 Client 执行交互式事务,或兼容 node-postgres API。在无服务器环境中,必须在单个请求处理程序内创建并关闭连接。
import { Pool } from "@neondatabase/serverless";
export async function handleRequest(request: Request): Promise<Response> {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
// ... queries ...
} finally {
await pool.end(); // ALWAYS close within the same request
}
}
关键规则: 在处理程序内创建 Pool,并在 finally 中调用 pool.end() 将其关闭。全局 Pool 会导致 WebSocket 连接在无服务器调用之间泄漏。
请参阅 examples/core.md 中的模式 5–7,了解请求范围的连接池、交互式事务和 Node.js WebSocket 配置。
模式 5:配置连接字符串
Neon 提供两种连接字符串格式:连接池(通过 PgBouncer)和直连。
# Pooled connection (note: -pooler suffix on endpoint ID)
# Use for: serverless functions, web apps, high-concurrency workloads
DATABASE_URL=postgresql://user:[email protected]/dbname?sslmode=require
# Direct connection (no -pooler suffix)
# Use for: migrations, pg_dump, LISTEN/NOTIFY, session-level features
DIRECT_DATABASE_URL=postgresql://user:[email protected]/dbname?sslmode=require
优点: 连接池通过事务模式下的 PgBouncer 支持多达 10,000 个并发客户端;直连为管理任务提供完整的会话功能;使用单独的环境变量可明确区分两者
PgBouncer 事务模式的限制
连接池在 事务模式 下运行 PgBouncer,这意味着每次事务结束后,连接都会返回连接池。因此,以下操作不受支持:
SET/RESET语句(请改用ALTER ROLE ... SET)LISTEN/NOTIFYWITH HOLD CURSOR- SQL 级别的
PREPARE/DEALLOCATE(支持最多 1,000 个协议级别的预处理语句) - 使用
PRESERVE/DELETE ROWS的临时表 - 会话级别的 advisory 锁
模式 6:处理冷启动和缩容至零
Neon 默认会在闲置 5 分钟后自动挂起。冷启动需要 200–500ms。请设置适当的超时和重试逻辑来应对这一情况。
const CONNECTION_TIMEOUT_MS = 10_000;
const sql = neon(process.env.DATABASE_URL!, {
fetchOptions: { signal: AbortSignal.timeout(CONNECTION_TIMEOUT_MS) },
});
关键规则: 设置明确的超时时间(10–15s),以应对冷启动延迟。使用指数退避重试来提高可靠性。
请参阅 examples/core.md 中的模式 8,了解完整的超时配置和重试模式。
缩容至零须知
- 默认自动挂起时间:闲置 5 分钟
- 冷启动延迟:约 200–500ms(因区域和计算规模而异)
- 仅适用于不超过 16 CU 的计算资源(更大的计算资源会始终保持运行)
- 付费计划可调整挂起超时时间(最长 7 天)或完全禁用
- 活跃的逻辑复制订阅方会阻止挂起
模式 7:数据库分支
Neon 分支采用写时复制语义——分支一个 500GB 的数据库会立即完成,并且在数据发生分歧之前不会分配额外存储空间。
# Install neonctl
npm install -g neonctl
# Create a dev branch from main
neonctl branches create --name dev-alice --project-id <project-id>
# Create a branch with automatic expiration (for CI/preview)
neonctl branches create --name preview/pr-42 --project-id <project-id> --expires-at "2025-04-01T00:00:00Z"
# Create schema-only branch (no data copied -- for sensitive environments)
neonctl branches create --name ci-test --schema-only --project-id <project-id>
# Reset a dev branch to match current production
neonctl branches reset dev-alice --parent --project-id <project-id>
# Delete a branch
neonctl branches delete preview/pr-42 --project-id <project-id>
优点: 命名分支与 Git 工作流相对应;TTL 到期后会自动清理 CI 分支;仅架构分支可保护敏感数据;重置可将开发环境与生产环境同步,而无需重新创建
分支连接字符串
每个分支都有自己的端点。分支连接字符串格式相同,但端点 ID 不同:
# Main branch
postgresql://user:[email protected]/dbname
# Dev branch -- different endpoint ID
postgresql://user:[email protected]/dbname
模式 8:Neon API,用于以编程方式管理分支
Neon REST API(https://console.neon.tech/api/v2)支持以编程方式管理分支。使用 Authorization: Bearer <NEON_API_KEY> 进行身份验证。主要操作:创建带有 TTL 过期时间的分支、删除分支,以及列出分支以供清理脚本使用。
const NEON_API_BASE = "https://console.neon.tech/api/v2";
// POST /projects/{projectId}/branches -- create with { branch: { name, expires_at }, endpoints: [{ type: "read_write" }] }
// DELETE /projects/{projectId}/branches/{branchId} -- delete a branch
请参阅 examples/branching.md 中的模式 3,了解包含创建、删除和列出操作的完整类型化 TypeScript 分支管理器。 请参阅 reference.md 获取完整的 API 端点表。
<decision_framework>
决策框架
HTTP(neon())与 WebSocket(Pool/Client)
What kind of database operation?
+-- Single query (SELECT, INSERT, UPDATE, DELETE)
| +-- YES --> Use neon() HTTP function (fastest, ~3 round trips)
+-- Multiple queries that must be atomic?
| +-- Can all queries be determined upfront (non-interactive)?
| | +-- YES --> Use sql.transaction() over HTTP
| | +-- NO --> Use Pool/Client over WebSocket
+-- Need node-postgres (pg) API compatibility?
| +-- YES --> Use Pool/Client over WebSocket
+-- Running in edge runtime (no TCP)?
+-- YES --> Use @neondatabase/serverless (HTTP or WebSocket)
+-- NO --> Standard pg driver with TCP may be simpler
连接池与直接连接
What is the workload?
+-- Serverless function / edge function --> Pooled (-pooler)
+-- Web application (many concurrent requests) --> Pooled (-pooler)
+-- Schema migration --> Direct (needs session state)
+-- pg_dump / pg_restore --> Direct (uses SET statements)
+-- LISTEN / NOTIFY --> Direct (session-level feature)
+-- Long-running analytics query --> Direct (avoid pool contention)
+-- Default / unsure --> Pooled (-pooler)
分支策略
What do you need the branch for?
+-- Developer working on a feature --> Dev branch (long-lived, manually managed)
+-- PR preview environment --> Preview branch (TTL expiration, auto-cleanup on merge)
+-- CI test run --> Ephemeral branch (short TTL, schema-only if data-sensitive)
+-- Database recovery --> Restore from branch history (up to 30 days on Scale plan)
+-- Load testing --> Branch from production (copy-on-write, no storage cost until diverge)
</decision_framework>
<red_flags>
危险信号
高优先级问题:
- 在无服务器环境中使用全局
Pool—— 在边缘/无服务器函数中,于请求处理程序外创建 Pool 会导致 WebSocket 连接泄漏。Pool/Client 必须在单个请求内创建、使用并关闭。 - 在无服务器环境中使用直接连接字符串 —— 直接连接会绕过 PgBouncer,且最大连接数受计算资源规格限制(100-4,000)。无服务器函数应始终使用池化(
-pooler)连接。 - 将用户输入传递给
sql.unsafe()——sql.unsafe()会嵌入未经参数化处理的原始 SQL。它仅用于可信标识符(表名/列名)。在sql.unsafe()中使用用户输入会造成 SQL 注入漏洞。
中优先级问题:
- 重复连接池 —— 将 Neon 服务端的 PgBouncer 与驱动程序中的客户端连接池结合使用,会产生不必要的开销。交由 Neon 处理连接池。
- 在无服务器环境中忽略
pool.end()—— 使用 WebSocket 连接后忘记调用pool.end(),会耗尽各次调用间可用的连接。 - 通过池化连接执行
SET语句 —— PgBouncer 事务模式会在每个事务后重置会话状态。角色级默认值请使用ALTER ROLE ... SET,或使用直接连接。 - 未处理冷启动延迟 —— 闲置一段时间后的首次请求会额外耗时 200-500ms。如果没有设置合适的超时时间(10 秒以上)和重试逻辑,应用会间歇性地失败。
常见错误:
- 包名错误 —— 正确的包名是
@neondatabase/serverless,不是neon-serverless或pg-neon。 - Node.js <= v21 缺少
ws包 —— v22 之前的 Node.js 版本没有内置 WebSocket 支持。使用Pool/Client时,请安装ws并设置neonConfig.webSocketConstructor = ws。Node.js v22+ 支持原生 WebSocket,无需额外配置。 - 将
neon()的结果作为函数调用,而不是标签模板 —— 自 v1.0 起,sql("SELECT ...")是类型错误。请使用sql`SELECT ...`(标签模板)。 - 误以为 Pool 会在多次无服务器调用间持续存在 —— 每次冷启动都会创建新的执行上下文。请勿依赖全局状态来管理连接。
- 64MB 请求/响应限制 —— HTTP 模式的载荷上限为 64MB。大型结果集或批量插入必须分块处理。
注意事项与边缘情况:
- 事务选项适用于事务,而非单条查询 —— 在
sql.transaction()中的单条查询上设置arrayMode: true会被忽略。请在事务本身上设置。 - PgBouncer 的 120 秒查询等待超时 —— 如果所有池化连接都处于忙碌状态,新查询最多会排队等待 120 秒,然后超时。
- 分支端点与父分支不同 —— 每个分支都会获得唯一的端点 ID。不能使用父分支的连接字符串连接到子分支。
- 仅计算资源规格 <= 16 CU 时会缩容至零 —— 计算资源规格大于 16 CU 时,无论配置如何都会保持运行。
- 逻辑复制会阻止暂停 —— 活跃的复制订阅端会使计算资源保持运行,从而绕过缩容至零。
- 仅含架构的分支 —— 使用
neonctl branches create --schema-only,或在 REST API 中使用"init_source": "schema-only"。仅含架构的分支必须恰好有一个读写计算端点。 - 分支历史记录有保留期限 —— 免费方案:6 小时。Launch:7 天。Scale:30 天。无法恢复超出此期限的历史记录。
- 需要 Node.js v19+ ——
@neondatabase/serverless的 GA 版本(v1.0+)要求 Node.js 19 或更高版本。
</red_flags>
<critical_reminders>
关键提醒
(在边缘/无服务器运行时中执行单次查询时,必须使用 neon() HTTP 函数——对于一次性操作,它比 WebSocket 快 2–3 倍。)
(在无服务器环境中,必须在同一个请求处理程序内关闭 Pool/Client 连接——WebSocket 连接无法在单个请求结束后继续存活。)
(无服务器工作负载必须使用连接池连接字符串(-pooler 后缀)——直接连接会耗尽有限的连接槽位。)
(必须使用适当的连接超时和重试逻辑,处理缩容至零后的唤醒延迟(200–500 毫秒)。)
(只能将 sql.unsafe() 用于表名、列名等可信且已知安全的字符串——绝不能用于用户输入。)
不遵守这些规则会导致连接耗尽、SQL 注入漏洞或间歇性冷启动故障。
</critical_reminders>