mcpField
N
netlify-database

netlify-database

github.com/netlify数据与数据库473.0k 次安装6 天前更新
代码仓库

关于 netlify-database

介绍如何在 Netlify 项目中使用托管 Postgres 数据库,涵盖查询、迁移、Drizzle 集成、本地开发和数据库 CLI 工作流。

主要功能

  1. 01托管 Postgres 配置
  2. 02参数化 SQL 查询
  3. 03生成并部署数据库迁移
  4. 04Drizzle ORM 适配器说明
  5. 05本地数据库与 CLI 工作流

适用场景

  1. 01为 Netlify 项目添加 Postgres
  2. 02从 Functions 或构建任务查询数据
  3. 03通过迁移管理架构变更
  4. 04使用本地数据库进行开发和测试

安装

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

$npx skills add https://github.com/netlify/context-and-tools --skill netlify-database

安装后在 Claude Code 中输入/netlify-database确认已加载。

包含的文件

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

references/
cli-commands.md
legacy-extension.md
local-dev.md
migration-from-extension.md
migrations.md
operational-footguns.md

详细介绍

Netlify Database

零配置托管 Postgres。安装 @netlify/database,在 netlify/database/migrations/ 下编写迁移,然后部署——Netlify 会自动配置数据库并应用迁移。可从 Functions、Edge Functions、Builds 和 Agent Runners 查询。

现代客户端(优先使用此方式)

import { getDatabase } from "@netlify/database";

const db = getDatabase();               // auto-selects connection for the runtime
const userId = 42;
const users = await db.sql`SELECT * FROM users WHERE id = ${userId}`;  // auto-parameterized

改用自有驱动程序/ORM:

import { getConnectionString } from "@netlify/database";
const connectionString = getConnectionString();  // correct branch for this env

旧版 — 新代码中请勿使用: import { neon } from "@netlify/neon"。已由 @netlify/database 取代。将 neon() 调用替换为 Drizzle netlify-db 适配器,或通过 getConnectionString() 使用 Postgres 驱动程序。旧环境变量 NETLIFY_DATABASE_URL 已替换为 NETLIFY_DB_URL。

文件放置位置

内容位置
迁移netlify/database/migrations/(SQL 文件,或包含 migration.sql 的子目录)
查询代码Functions(netlify/functions/)、Edge Functions
Drizzle 架构db/schema.ts(惯例)
Drizzle 客户端db/index.ts(惯例)
连接字符串NETLIFY_DB_URL 环境变量,或 getConnectionString()

查询

getDatabase(options?) 会返回包含 sql 和 pool 的客户端。options.connectionString 会覆盖自动配置的连接字符串;options.debug 会启用日志记录。

const db = getDatabase();
const active = await db.sql`SELECT * FROM users WHERE active = ${true}`;
await db.sql`INSERT INTO users (name, email) VALUES (${"Ada"}, ${"[email protected]"})`;
await db.sql`UPDATE users SET name = ${"Ada Lovelace"} WHERE id = ${1}`;
await db.sql`DELETE FROM users WHERE id = ${1}`;

// Type the rows
interface User { id: number; name: string; email: string; }
const typed = await db.sql<User>`SELECT * FROM users`;

// Stream
for await (const row of db.sql`SELECT * FROM users`.stream()) { /* ... */ }
for await (const chunk of db.sql`SELECT * FROM users`.chunked(100)) { /* ... */ }

SQLTemplate 方法:execute() → Promise<T[]>,stream() → AsyncGenerator<T>,chunked(n) → AsyncGenerator<T[]>,toSQL() → 返回原始 SQL 和参数,但不执行查询。

sql 辅助项:

  • sql.identifier(value) — 安全的表名/列名。可接受字符串、string[] 或 { schema, table, column, as }。
  • sql.values(rows) — 根据二维数组生成批量插入值列表。
  • sql.default — SQL DEFAULT 关键字。
  • sql.raw(value) — 注入未参数化的 SQL;会绕过注入保护。仅用于可信常量(例如 "DESC"),绝不可用于用户输入。
  • sql.unsafe(query, params?, { rowMode }) — 使用 $1 参数的原始查询字符串;rowMode 为 "array" 或 "object"。
事务 — 使用 pool

db.pool 是 pg.Pool。BEGIN/查询/COMMIT 必须在同一连接上运行:

const client = await db.pool.connect();
try {
  await client.query("BEGIN");
  await client.query("INSERT INTO users (name, email) VALUES ($1, $2)", ["Ada", "[email protected]"]);
  await client.query("INSERT INTO posts (author_id, title) VALUES ($1, $2)", [1, "First post"]);
  await client.query("COMMIT");
} catch (e) {
  await client.query("ROLLBACK");
  throw e;
} finally {
  client.release();
}

自有驱动程序:

import { getConnectionString } from "@netlify/database";
import pg from "pg";
const pool = new pg.Pool({ connectionString: getConnectionString() });

// or the `postgres` driver via env var
import postgres from "postgres";
const sql = postgres(process.env.NETLIFY_DB_URL);

Drizzle ORM

必须从 @beta 安装这两个包。 latest 不包含 drizzle-orm/netlify-db 适配器,因此会失败。

npm install @netlify/database drizzle-orm@beta
npm install -D drizzle-kit@beta

drizzle.config.ts — 你必须将 out 设置为 Netlify 迁移目录,否则 Netlify 不会应用生成的迁移:

import { defineConfig } from "drizzle-kit";
export default defineConfig({
  dialect: "postgresql",
  schema: "./db/schema.ts",
  out: "netlify/database/migrations",   // NOT the default "drizzle"
});
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
  id: serial().primaryKey(),
  name: text().notNull(),
  email: text().notNull().unique(),
  createdAt: timestamp().defaultNow(),
});
import { drizzle } from "drizzle-orm/netlify-db";  // native adapter, auto-configured
import * as schema from "./schema";
export const db = drizzle({ schema });
import { desc } from "drizzle-orm";
import type { Config, Context } from "@netlify/functions";
import { db } from "../../db";
import { users } from "../../db/schema";

export default async (req: Request, context: Context) => {
  if (req.method === "GET") {
    const allUsers = await db.select().from(users).orderBy(desc(users.createdAt));
    return Response.json(allUsers);
  }
  if (req.method === "POST") {
    const { name, email } = await req.json();
    const [user] = await db.insert(users).values({ name, email }).returning();
    return Response.json(user, { status: 201 });
  }
  return new Response("Method not allowed", { status: 405 });
};

export const config: Config = { path: "/api/users" };

编辑架构后生成迁移:npx drizzle-kit generate。

绝不要对 Netlify 托管的数据库运行 drizzle-kit push,也绝不要对 NETLIFY_DB_URL 运行 drizzle-kit migrate。 架构只能通过已提交的迁移文件部署到托管数据库,并由部署过程应用。generate 会写入文件;部署过程会应用这些文件。

迁移

文件位于 netlify/database/migrations/。有两种格式:

netlify/database/migrations/20260301143000_create_users.sql          # single SQL file
netlify/database/migrations/20260318091500_add_posts/migration.sql   # subdir form

命名规则:<number>_<slug>——number 是数字(时间戳或 0001…),用于定义顺序;slug 由小写字母、数字、连字符和下划线组成。按字典序排序,并按顺序应用。使用时间戳前缀(netlify database migrations new 会自动处理),以避免因顺序错乱而被拒绝。

CREATE TABLE comments (
  id SERIAL PRIMARY KEY,
  post_id INTEGER NOT NULL REFERENCES posts(id),
  author_id INTEGER NOT NULL REFERENCES users(id),
  body TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

应用时机:

  • 生产部署:在发布前立即应用;如果失败,则阻止发布。关闭自动发布时,Netlify 会等待手动发布后再应用。
  • 部署预览:每次部署上线前都会应用;如果失败,则部署失败。
  • 本地:不会自动应用——请自行运行 netlify database migrations apply。

迁移易错点(均会被检测为偏移 / 拒绝):

  • 绝不要编辑已应用的迁移——校验和偏移:migration "<name>" has been modified after being applied。请编写新的修正迁移。
  • 绝不要删除已应用的迁移——... has been removed after being applied。请将其恢复。
  • **顺序错乱:**前缀 ≤ 最高已应用版本时会被拒绝。使用时间戳可避免此问题。
  • 优先使用向后兼容的迁移。破坏性变更(重命名 / 删除列)→ 跨多个部署采用扩展-收缩模式。新建表 / 可空列 → 使用单个迁移即可。

自带迁移系统:选择 netlify/database/migrations 以外的目录,避免自动检测;并由你负责将迁移应用到预览分支和生产环境。

请参阅 references/migrations.md。

本地开发

本地只有一个数据库,所有代码都以它为目标——分支是部署时的概念,在本地并不存在。它是与生产环境镜像一致的真实 Postgres 兼容引擎,但以单进程运行(不适用于负载测试);自动扩缩容 / 休眠设置不适用。

启动它——任选一种方式,状态可互换:

netlify dev                                    # CLI starts + tears down the local DB

或者使用 Vite 插件:

import { defineConfig } from "vite";
import netlify from "@netlify/vite-plugin";
export default defineConfig({ plugins: [netlify()] });

常用命令(本地数据库运行期间):

netlify database migrations apply                        # apply pending locally
netlify database migrations new -d "add users table"     # scaffold new migration
netlify database migrations pull                          # overwrite local migrations from remote
netlify database status                                   # enabled? installed? applied/pending migrations
netlify database connect                                  # interactive SQL REPL
netlify database connect --query "SELECT * FROM users LIMIT 10"
netlify database reset                                    # drop all schemas/tables — LOCAL ONLY
netlify database migrations reset                         # delete unapplied local migration files

外部工具(netlify dev 运行期间可用):

psql "$(netlify database connect --json | jq -r .connection_string)"

请参阅 references/local-dev.md。

设置

新项目:在 https://app.netlify.com/start 向 Agent Runners 描述你的应用,或在本地运行 netlify create "<description>"。

现有项目:

netlify database init      # installs @netlify/database, picks Drizzle or raw SQL, scaffolds a migration
netlify database init --yes # non-interactive (CI / agents)
netlify dev

手动设置:运行 npm install @netlify/database,在 netlify/database/migrations/ 下编写迁移,编写函数,运行 netlify dev,然后部署。

如果未安装 @netlify/database,Netlify 将不会自动配置数据库——你必须通过 UI 中的 Data & Storage > Database 菜单手动创建数据库。请安装此软件包。

CLI 参考(netlify database)

前置条件:Node ≥ 20.12.2、Netlify CLI ≥ 26.0.0(npm install -g netlify-cli)。所有命令都支持 --json。

命令用途关键标志
init在项目中设置数据库-y, --yes
status状态:已启用、已安装、连接字符串、已应用 / 待处理的迁移-b, --branch、--show-credentials
connectSQL REPL,或使用 --query 执行一次性查询-q, --query、--json
migrations apply将待处理迁移应用到本地数据库--to <name>
migrations new创建迁移脚手架-d, --description、-s, --scheme sequential|timestamp
migrations pull用分支中的文件覆盖本地文件-b, --branch、--force
migrations reset删除尚未应用的本地迁移文件-b, --branch
reset删除所有数据 / 表——仅限本地—

请参阅 references/cli-commands.md。

REST API

限定于某个站点,根地址为 https://api.netlify.com/api/v1,OAuth 2。完整参考:https://open-api.netlify.com。

方法 + 路径用途
POST /sites/{site_id}/database创建数据库(如果已存在,则返回现有连接字符串);region 可选
GET /sites/{site_id}/database获取连接字符串
POST /sites/{site_id}/database/branch创建分支;请求体为 deploy_id(必需)、parent_branch_id(可选,默认为生产分支)
GET /sites/{site_id}/database/branch/{deploy_id}获取分支连接字符串(不存在时返回 404)
DELETE /sites/{site_id}/database/branch/{deploy_id}删除某次部署的分支
POST /sites/{site_id}/database/snapshot创建分支快照(默认为生产分支)
GET /sites/{site_id}/database/snapshots列出快照
DELETE /sites/{site_id}/database/snapshot/{snapshot_id}删除快照
POST /sites/{site_id}/database/snapshot/{snapshot_id}/restore将快照还原到某个分支(默认为生产分支)

删除分支和还原快照具有破坏性,必须先征得用户明确确认。 快照还原不应作为常规的生产回滚手段。

测试

用于单元/集成测试的纯 Postgres(不含函数):

import { NetlifyDB } from "@netlify/database-dev";  // npm i -D @netlify/database-dev
import { Client } from "pg";
import { afterAll, beforeAll, expect, test } from "vitest";

let db: NetlifyDB, connectionString: string;
beforeAll(async () => {
  db = new NetlifyDB();
  connectionString = await db.start();
  await db.applyMigrations("./netlify/database/migrations");
});
afterAll(async () => { await db.stop(); });

test("inserts and reads a user", async () => {
  const client = new Client({ connectionString });
  await client.connect();
  await client.query("INSERT INTO users (name) VALUES ($1)", ["Ada"]);
  const { rows } = await client.query("SELECT name FROM users");
  expect(rows).toEqual([{ name: "Ada" }]);
  await client.end();
});

NetlifyDB(options?):directory(持久化到磁盘;省略则使用内存)、port(默认为随机端口)、logger。

完整的 Netlify 环境(函数/边缘函数会像生产环境一样读取 NETLIFY_DB_URL):

import { NetlifyDev } from "@netlify/dev";  // npm i -D @netlify/dev
const netlifyDev = new NetlifyDev({ projectRoot: "./fixtures/my-project" });
await netlifyDev.start();  // sets NETLIFY_DB_URL in the runtime
// ...tests...
await netlifyDev.stop();

数据库分支(部署时)

只有生产部署会访问生产数据库。每个部署预览都会获得自己的分支,并在创建预览时复制一份生产数据;在该分支中进行的架构/数据更改不会影响生产环境。自动接入,无需更改代码。

预览分支可能包含生产数据,包括 PII;而且部署预览链接是公开的。分享预览链接前,请提醒用户。

运行时注意事项

  • Environment not configured(getDatabase() 无法解析连接字符串):在 Netlify 之外运行、使用 Lambda 兼容模式的 Functions,或使用过时的 CLI。解决方法:显式传入 connectionString。
    const db = getDatabase({ connectionString: "postgres://..." });
    
    Lambda 兼容模式是唯一必须自行传入 connectionString 的原始环境。
  • database feature not available for this account — 需要采用基于 Credit 的套餐。
  • compute customization requires a Pro or higher plan — 自动扩缩/休眠设置需要 Pro 或更高套餐;Free/Personal 使用默认设置。
  • branch limit reached: maximum <N> branches... — 每个活动的部署预览都会占用一个分支;删除不需要的分支或升级套餐。
  • database not found — 尚未配置数据库;运行 netlify database init。
  • cannot reset the production branch — 只能重置非生产分支。

限制

  • 套餐: Netlify Database 仅适用于基于 Credit 的套餐;活动数据库会消耗计算和带宽额度。存储免费至 2026 年 7 月 1 日。
  • 权限: 只有 Team Owner 可以删除数据库;只有 Team Owner 和 Developer 可以查看连接字符串(Access Denied 表示角色权限不足)。
  • 密钥: 连接字符串包含用户名和密码。切勿将其提交到代码库;请存储在密钥管理器/环境变量提供方中。

将现有 Postgres 项目切换到 Netlify Database

分为三个阶段:配置(在分支上创建基准架构)、演练(替换代码,将数据复制到预览分支并验证)、切换(将数据导入生产环境并合并)。适用于任何 Postgres 来源(Neon、Supabase、RDS、自行管理的实例、旧版 @netlify/neon)。使用 pg_dump/pg_restore(版本需与来源匹配)。切换期间会有短暂的数据丢失窗口——最终导出后、生产部署前写入来源的数据不会同步过来。

阶段 2/3 代码替换(Drizzle):

import { drizzle } from "drizzle-orm/netlify-db";
import * as schema from "./schema";
export const db = drizzle({ schema });

完整的分步说明(转储标志、回滚、清理):references/migration-from-extension.md 和 references/legacy-extension.md。

Netlify 规范(数据库)

这些是组织约定,并非文档事实——它们由 ctx-gen 合并到渲染后的技能中,且不会自动生成。由技能维护者负责。

  1. 生产数据变更必须通过 DML 迁移表达——Agent 绝不能直接编辑行(虽然人类可以在 UI 中编辑行,但这不是面向 Agent 的操作界面)。
  2. 预览分支可能包含生产数据,包括 PII,且预览部署链接是公开的。分享前请发出警告。
  3. 只能使用有文档说明的操作界面:不得通过内部端点运行原始 psql,不得抓取 netlify api,也不得读取本地 CLI 配置文件中的令牌。
  4. 深入指南位于此技能中:references/operational-footguns.md、references/migrations.md、references/local-dev.md、references/cli-commands.md、references/migration-from-extension.md、references/legacy-extension.md。
  5. 架构变更只能通过已提交的迁移文件,由部署应用到托管数据库。绝不能以任何形式对 Netlify 托管的数据库运行 drizzle-kit push,绝不能对 NETLIFY_DB_URL 运行 drizzle-kit migrate,也绝不能通过 netlify database connect 或任何直接连接应用 DDL。
  6. 如果 netlify 命令或部署失败,向用户展示确切的错误、部署日志 URL 以及受影响的站点/分支,然后停止——不要编造恢复命令,也不要升级到更底层的工具。
  7. 首次部署时,createSiteDatabase 出现 401 Access Denied:如果发生在先执行 --prod 的部署中,则改为先部署预览(netlify deploy,不带 --prod)并重试;如果预览也失败,则报告并停止。绝不能通过 curl api.netlify.com、运行 netlify api createSiteDatabase 或从本地 CLI 配置中提取令牌来绕过问题。
  8. 更改现有数据的请求可能指生产环境,也可能指预览分支——如果提示词未说明,请先询问。代替他人操作时,默认不触碰生产环境。
  9. 破坏性数据库操作——REST 分支删除、快照恢复以及任何重置——都必须先获得用户明确确认。正文不得将快照恢复描述为常规的生产回滚手段。
  10. 固定版本:drizzle-orm 和 drizzle-kit 必须从 @beta 安装——latest 缺少 drizzle-orm/netlify-db 适配器,会导致失败。正文不得将此要求弱化为建议。

评论

支持 Markdown