name: neon-postgres description: Neon PostgreSQL 无服务器数据库 - 连接池、分支、无服务器驱动程序和优化。适用于部署到 Neon 或构建无服务器应用程序。
Neon PostgreSQL 技能
具有分支、自动扩缩和即时配置功能的无服务器 PostgreSQL。
快速开始
创建数据库
- 访问 console.neon.tech
- 创建一个新项目
- 复制连接字符串
安装
# npm
npm install @neondatabase/serverless
# pnpm
pnpm add @neondatabase/serverless
# yarn
yarn add @neondatabase/serverless
# bun
bun add @neondatabase/serverless
连接字符串
# 直接连接(用于迁移、脚本)
DATABASE_URL=postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require
# 池化连接(用于应用程序)
DATABASE_URL_POOLED=postgresql://user:password@ep-xxx-pooler.us-east-1.aws.neon.tech/dbname?sslmode=require
核心概念
| 概念 | 指南 |
|---|---|
| 无服务器驱动程序 | reference/serverless-driver.md |
| 连接池 | reference/pooling.md |
| 分支 | reference/branching.md |
| 自动扩缩 | reference/autoscaling.md |
示例
| 模式 | 指南 |
|---|---|
| Next.js 集成 | examples/nextjs.md |
| 边缘函数 | examples/edge.md |
| 数据库迁移 | examples/migrations.md |
| 分支工作流 | examples/branching-workflow.md |
模板
| 模板 | 用途 |
|---|---|
| templates/db.ts | 数据库连接 |
| templates/neon.config.ts | Neon 配置 |
连接方法
HTTP(无服务器 - 推荐)
最佳适用:边缘函数、无服务器、一次性查询
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
// 简单查询
const posts = await sql`SELECT * FROM posts WHERE published = true`;
// 带参数查询
const post = await sql`SELECT * FROM posts WHERE id = ${postId}`;
// 插入数据
await sql`INSERT INTO posts (title, content) VALUES (${title}, ${content})`;
WebSocket(连接池)
最佳适用:长连接、事务处理
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query("INSERT INTO posts (title) VALUES ($1)", [title]);
await client.query("COMMIT");
} catch (e) {
await client.query("ROLLBACK");
throw e;
} finally {
client.release();
}
与 Drizzle ORM 集成
HTTP 驱动程序
// src/db/index.ts
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
WebSocket 驱动程序
// src/db/index.ts
import { Pool } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
import * as schema from "./schema";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool, { schema });
分支功能
Neon 分支是数据库的写时复制克隆。
CLI 命令
# 安装 Neon CLI
npm install -g neonctl
# 登录
neonctl auth
# 列出分支
neonctl branches list
# 创建分支
neonctl branches create --name feature-x
# 获取连接字符串
neonctl connection-string feature-x
# 删除分支
neonctl branches delete feature-x
分支工作流
# 为功能创建分支
neonctl branches create --name feature-auth --parent main
# 获取分支的连接字符串
export DATABASE_URL=$(neonctl connection-string feature-auth)
# 开发功能...
# 完成后,通过应用迁移合并
neonctl branches delete feature-auth
CI/CD 集成
# .github/workflows/preview.yml
name: Preview
on: pull_request
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Neon Branch
uses: neondatabase/create-branch-action@v5
id: branch
with:
project_id: ${{ secrets.NEON_PROJECT_ID }}
api_key: ${{ secrets.NEON_API_KEY }}
branch_name: preview-${{ github.event.pull_request.number }}
- name: Run Migrations
env:
DATABASE_URL: ${{ steps.branch.outputs.db_url }}
run: npx drizzle-kit migrate
连接池
何时使用连接池
| 场景 | 连接类型 |
|---|---|
| 边缘/无服务器函数 | HTTP (neon) |
| 涉及事务的 API 路由 | WebSocket 池 |
| 长时间运行的进程 | WebSocket 池 |
| 一次性查询 | HTTP (neon) |
池化器 URL
# 不使用池化器(直接连接)
postgresql://user:pass@ep-xxx.aws.neon.tech/db
# 使用池化器(在端点后添加 -pooler)
postgresql://user:pass@ep-xxx-pooler.aws.neon.tech/db
自动扩缩
在 Neon 控制台配置:
- 最小计算单元:0.25 CU(可缩容至零)
- 最大计算单元:最高 8 CU
- 缩容至零延迟:5 分钟(默认)
处理冷启动
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!, {
fetchOptions: {
// 为冷启动增加超时时间
signal: AbortSignal.timeout(10000),
},
});
最佳实践
1. 在无服务器环境中使用 HTTP
// 好 - 无服务器环境使用 HTTP
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
// 避免 - 在无服务器环境中使用连接池(可能导致连接耗尽)
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
2. 按环境配置连接字符串
# .env.development
DATABASE_URL=postgresql://...@ep-dev-branch...
# .env.production
DATABASE_URL=postgresql://...@ep-main...
3. 使用参数化查询
// 好 - 参数化查询
const result = await sql`SELECT * FROM users WHERE id = ${userId}`;
// 不好 - 字符串拼接(有 SQL 注入风险)
const result = await sql(`SELECT * FROM users WHERE id = '${userId}'`);
4. 错误处理
import { neon, NeonDbError } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
try {
await sql`INSERT INTO users (email) VALUES (${email})`;
} catch (error) {
if (error instanceof NeonDbError) {
if (error.code === "23505") {
// 唯一性约束冲突
throw new Error("邮箱已存在");
}
}
throw error;
}
Next.js App Router
// app/posts/page.tsx
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
export default async function PostsPage() {
const posts = await sql`SELECT * FROM posts ORDER BY created_at DESC`;
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Drizzle + Neon 完整设置
// src/db/index.ts
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql, { schema });
// src/db/schema.ts
import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core";
export const posts = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
content: text("content"),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
// drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/db/schema.ts",
out: "./src/db/migrations",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});