name: api-patterns description: 使用 RLS、Zod 验证和错误处理的 API 路由实现模式。在创建 API 路由、实现端点或添加服务器端验证时使用。
API 模式技能
目的
路由到现有 API 模式,并为安全、验证的 API 路由实现提供检查清单。所有 API 路由必须使用 RLS 上下文助手——参见 rls-patterns 技能。
适用场景
在以下情况下调用此技能:
- 创建新的 API 路由
- 实现 CRUD 端点
- 添加请求/响应验证
- 处理 webhooks
- 实现错误处理模式
权威参考资料(必须阅读)
| 模式 | 位置 | 目的 |
|---|---|---|
| 用户上下文 API | docs/patterns/api/user-context-api.md |
用户范围的操作 |
| 管理员上下文 API | docs/patterns/api/admin-context-api.md |
管理员范围的操作 |
| Zod 验证 | docs/patterns/api/zod-validation-api.md |
请求/响应验证 |
| Webhook 处理程序 | docs/patterns/api/webhook-handler.md |
Webhook 处理 |
| 奖励内容 | docs/patterns/api/bonus-content-delivery.md |
受保护内容交付 |
停止条件
禁止模式
// 禁止:直接 Prisma 调用(绕过 RLS)
const users = await prisma.user.findMany();
// 必须使用:withUserContext、withAdminContext 或 withSystemContext
// 禁止:缺少身份验证检查
export async function GET(req: Request) {
return getUserData(); // 没有身份验证检查!
}
// 禁止:未验证的用户输入
const { userId } = await req.json();
// 必须使用 Zod 模式验证
// 禁止:通用错误响应
return new Response("错误", { status: 500 });
// 必须使用结构化错误响应
正确模式
// 正确:RLS 上下文 + 身份验证检查
export async function GET(req: Request) {
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "未经授权" }, { status: 401 });
}
const data = await withUserContext(prisma, userId, async (client) => {
return client.user.findUnique({ where: { user_id: userId } });
});
return NextResponse.json(data);
}
// 正确:Zod 验证
const schema = z.object({
email: z.string().email(),
name: z.string().min(1),
});
const result = schema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: "验证失败", details: result.error.flatten() },
{ status: 400 },
);
}
API 路由检查清单
在任何 API 路由之前:
- [ ] 使用 Clerk 的
await auth()进行身份验证检查 - [ ] 对未经身份验证的请求提供适当的 401 响应
- [ ] 使用 Zod 模式进行请求验证
- [ ] RLS 上下文包装器(
withUserContext/withAdminContext/withSystemContext) - [ ] 结构化错误响应,包含适当的状态码
- [ ] 请求/响应的 TypeScript 类型
标准响应模式
成功响应
return NextResponse.json({ data, success: true }, { status: 200 });
错误响应
return NextResponse.json(
{
error: "人类可读的错误消息",
code: "ERROR_CODE",
details: optional_details,
},
{ status: 400 | 401 | 403 | 404 | 500 },
);
状态码
| 代码 | 何时使用 |
|---|---|
| 200 | 成功 |
| 201 | 创建(POST) |
| 400 | 错误请求 / 验证错误 |
| 401 | 未认证 |
| 403 | 禁止(已认证但未授权) |
| 404 | 资源未找到 |
| 500 | 服务器错误 |
API 路由模板
import { auth } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { z } from "zod";
import { withUserContext } from "@/lib/rls-helpers";
import { prisma } from "@/lib/prisma";
// 请求验证模式
const RequestSchema = z.object({
// 定义期望字段
});
export async function POST(req: Request) {
try {
// 1. 身份验证
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: "未经授权" }, { status: 401 });
}
// 2. 解析和验证请求
const body = await req.json();
const result = RequestSchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ error: "验证失败", details: result.error.flatten() },
{ status: 400 },
);
}
// 3. 使用 RLS 上下文执行
const data = await withUserContext(prisma, userId, async (client) => {
return client.resource.create({ data: result.data });
});
// 4. 返回成功响应
return NextResponse.json({ data, success: true }, { status: 201 });
} catch (error) {
console.error("API 错误:", error);
return NextResponse.json(
{ error: "内部服务器错误" },
{ status: 500 },
);
}
}
API 文档模板
用于记录新端点:
## 端点:POST /api/resource
### 描述
为认证用户创建新资源。
### 身份验证
必需:Clerk 会话
### 请求体
| 字段 | 类型 | 必填 | 描述 |
| ----- | ------ | -------- | ------------- |
| 名称 | 字符串 | 是 | 资源名称 |
| 类型 | 字符串 | 否 | 资源类型 |
### 响应
**成功(201)**:
```json
{ "data": { "id": 1, "name": "..." }, "success": true }
错误(400):
{ "error": "验证失败", "details": {...} }
RLS 上下文
使用 withUserContext - 用户只能访问自己的资源。
## 相关技能
- **rls-patterns**:RLS 上下文助手用法(所有数据库操作必须)
- **security-audit**:API 安全验证
- **testing-patterns**:API 端点测试