mcpField
A
api-baas-neon

api-baas-neon

github.com/agents-inc数据与数据库2660 次安装2 天前更新
代码仓库

关于 api-baas-neon

面向使用 Neon 无服务器 PostgreSQL 驱动的开发者,介绍边缘与无服务器场景下的查询、连接及分支管理,并涵盖 SQL 安全实践。

主要功能

  1. 01使用 `neon()` 执行 HTTP 查询
  2. 02管理 WebSocket 连接池与客户端
  3. 03通过 HTTP 执行原子事务
  4. 04区分池化与直连方式
  5. 05创建数据库分支
  6. 06处理冷启动

适用场景

  1. 01在边缘函数中查询 PostgreSQL
  2. 02在无服务器处理程序中运行事务
  3. 03创建开发或预览分支

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/agents-inc/skills --skill api-baas-neon

安装后在 Claude Code 中输入/api-baas-neon确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.yaml
reference.md
examples/
branching.md
core.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/Client WebSocket 连接
  • 连接池(-pooler)与直连连接字符串,以及各自的适用场景
  • 数据库分支(开发分支、PR 预览分支、仅含架构的分支)
  • 缩容至零行为、冷启动缓解和自动扩缩容
  • 用于非交互式 HTTP 事务的 sql.transaction()
  • 使用 Neon API 和 neonctl CLI 以编程方式管理分支

不适用场景:

  • 传统的长生命周期服务器连接(使用标准 pg 驱动和 TCP)
  • ORM 特有的复杂模式(使用对应 ORM 的技能)
  • 通用 PostgreSQL 查询语法(使用 SQL/Postgres 技能)

详细资源:

  • 有关决策框架和快速查询表,请参阅 reference.md

驱动与查询:

分支与运维:


## 理念

Neon 将 PostgreSQL 的存储与计算分离,支持传统 Postgres 无法实现的无服务器功能:缩容至零、即时分支和自动扩缩容。@neondatabase/serverless 驱动将 TCP 替换为 HTTP 和 WebSockets,使 Postgres 可在不支持 TCP 的边缘运行时中使用。

核心原则:

  1. HTTP 追求速度,WebSocket 用于会话——neon() 函数对单条查询使用 HTTP fetch(约 3 次往返)。需要会话或交互式事务时,Pool/Client 使用 WebSockets(约 8 次往返)。应根据任务选择合适的传输方式。
  2. 默认使用连接池——池化连接通过 PgBouncer(事务模式)路由,可处理多达 10,000 个并发客户端。直接连接受计算资源规模限制(100-4,000),仅应用于迁移或需要会话状态的功能。
  3. 分支成本低——采用写时复制,因此 500GB 数据库的分支在数据出现差异前无需分配额外存储。可放心将分支用于开发、预览、测试和 CI。
  4. 默认缩容至零——计算资源在闲置 5 分钟后暂停。冷启动需要 200-500ms。设计时应考虑超时、重试和连接池。
  5. 内置 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 | Serializable
  • readOnly:布尔值(默认值为 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 / NOTIFY
  • WITH 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>

评论

支持 Markdown