关于 backend-api
面向设计和审查 HTTP API 的技能,适用于 Python 与 Node.js 服务开发者。覆盖 API 契约、认证边界、错误模型及框架实现,并提供逐步工作流与自检清单。
主要功能
- 01HTTP API 契约设计与审查
- 02分页、筛选与错误模型
- 03FastAPI、Express 和 NestJS 结构
- 04会话、OAuth 与令牌认证
- 05OpenAPI 文档和兼容性检查
适用场景
- 01规划新的 REST API
- 02审查现有 FastAPI 服务
- 03定义分页与错误响应规范
- 04设计客户端认证流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ iuliandita/ skills --skill backend-api安装后在 Claude Code 中输入/backend-api确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/agent-hygiene.mdauth-and-session-patterns.mdgraphql-grpc-patterns.mdhttp-api-patterns.mdoutput-contract.md详细介绍
后端 API:HTTP 服务设计与实现
设计和审查随规模增长仍保持一致的 HTTP API。重点关注契约、认证边界、错误模型,以及 Python 和 Node.js 服务的框架结构。
目标版本(2026 年 10 月):
- FastAPI 0.142.2
- Express 5.2.1(发布于 2025-12-01)
- NestJS 12.1.2(主版本发布;升级前请查看迁移说明;12.0.2+ 修复了高危 微服务 和 Fastify 中间件绕过安全公告)
- OpenAPI Specification 3.2.1(发布于 2026-09-10)
- HTTP 语义:RFC 9110(2022 年 6 月)
- HTTP API 问题详情:RFC 9457(2023 年 7 月)
- OAuth 2.0 安全最佳实践:RFC 9700(2025 年 1 月)
安全检查(2026-10-28):使用 Multer <2.3.0 的 Express/NestJS 上传技术栈会受到高危多部分请求拒绝服务漏洞的影响:CVE-2026-82333。
请将 Multer 升级到 2.5.0+(2.5.0 修复了请求拆除后仍有待处理异步 fileFilter 导致的高危 DoS 漏洞:CVE-2026-113872;2.4.0 修复了中危的中止上传导致磁盘耗尽漏洞:CVE-2026-88932),并根据应用需求限制 limits.fieldArrayIndexLimit;只检查顶层框架版本会漏掉存在漏洞的中间件。
此技能涵盖以下方面:
- 契约设计 - 资源、方法、状态码、架构、版本控制
- API 易用性 - 分页、筛选、排序、幂等性、错误格式
- 框架结构 - FastAPI 依赖项、Express 中间件、NestJS 模块和守卫
- 身份验证 - 会话、Bearer 令牌、OAuth/OIDC、BFF 和令牌中介
- 审查 - 不稳定的契约、DTO 泄漏、身份验证混淆和 HTTP 误用
- 有界协议模式 - 在 GraphQL 或 gRPC 已纳入范围时,提供 GraphQL 架构/解析器和 gRPC 契约、截止时间、重试及流式传输防护
适用场景
- 设计新的 REST 或 HTTP API
- 审查现有的 FastAPI、Express 或 NestJS 服务
- 编写或修复 OpenAPI 规范和生成的文档
- 选择状态码、错误格式、分页、筛选或版本控制策略
- 为浏览器、移动设备、机器间或第三方客户端设计身份验证流程
- 重构难以理解的路由或控制器结构
- 规划向后兼容的 API 演进
- 定义写入端点的幂等性和重试行为
不适用场景
- 独立的 GraphQL 联邦架构或协议级 gRPC 平台管理——请先缩小请求范围;此技能涵盖服务契约模式,不是框架目录
- 数据库架构、索引、复制或查询调优——使用 databases
- API 边界之外的一般错误查找、竞态条件排查或正确性审查——使用 code-review
- 针对身份验证绕过、注入、密钥或 OWASP 问题的安全审计——使用 security-audit
- 编写或调试自动化测试——使用 testing
- 部署、容器、网关、入口或集群配置——使用 docker、kubernetes 或 networking
- API 交付的 CI/CD 流水线设计——使用 ci-cd
- MCP 专用 HTTP 服务和工具处理程序——使用 mcp
AI 自检
在返回 API 代码、路由设计或 OpenAPI 输出之前,请确认:
- 资源名称使用名词,URL 结构稳定(
/users/{userId}/sessions,而非/doUserSessionThing) - 方法语义遵循 RFC 9110——不能使用会产生变更的“GET”,也不能把 PATCH 当作含糊不清的万能方法
-
401与403使用正确:前者表示未通过身份验证,后者表示已通过身份验证但无权访问 - 有意且一致地处理所有权隐藏行为:确定范围外资源何时返回
404,何时返回403 - 错误响应采用统一格式,优先使用 RFC 9457 问题详情格式
- 请求和响应 DTO 明确,并与 ORM 或数据库模型分开
- 即使客户端或 SDK 也会执行验证,仍须在服务端验证输入
- 对于大型集合或变化频繁的集合,不要盲目使用偏移分页;此类场景下游标分页通常更安全
- 可能会重试的写入端点(
POST /payments、webhook 接收器、订单创建)定义幂等行为 - OpenAPI 文档与实际处理程序行为、示例、状态码和身份验证要求一致
- 第一方浏览器应用默认不在浏览器存储中保存长期有效的 bearer token
- 基于 Cookie 的浏览器身份验证应考虑 Cookie 作用范围和 CSRF 行为,而非假设 Cookie 天然安全
- OAuth 指引符合当前规范:使用授权码 + PKCE,不使用隐式授权流程,也不使用资源所有者密码凭据
- 明确敏感项的默认设置:Cookie 标志、令牌 TTL、作用域边界和速率限制不能含糊带过
- 已检查框架版本:FastAPI、Express、NestJS 和 OpenAPI 示例与当前 API 及迁移说明一致
- 已检查契约兼容性:除非明确升级版本,否则响应码、分页、错误和身份验证行为应保持与现有客户端兼容
- 将处理程序主体中的注入标记为契约缺陷:如果在路由处理程序中发现注入(SQL、命令、标头、路径遍历),应将其标记为契约层缺少验证边界的缺陷,并建议在该层修复;如果此类模式普遍存在,则转交给 security-audit 进行全面注入审计
- 已应用跨领域 Agent 卫生规范——参见
references/agent-hygiene.md
性能
- 在数据存储层进行分页和筛选;切勿为了在处理程序中切片而加载整个集合。
- 在框架、代理和客户端层设置超时和请求体大小限制。
- 在优化内部实现之前,测量变更端点的 p95/p99 延迟和错误率。
最佳实践
- 在处理程序中使用明确的身份验证作用域和租户检查,而不只是依赖路由分组或 UI 状态。
工作流
**构建与审查:**审查现有服务时,仍应遵循相同步骤。确定 API 边界,审计契约,追踪身份验证,然后将实现与已发布的行为进行比较,而不是直接跳到处理程序代码。
顺序很重要:契约先于处理程序。复制并跟踪以下步骤:
- 步骤 1:明确边界、调用方、稳定性承诺和项目惯例
- 步骤 2:框架模式与代码库一致
- 步骤 3:定义契约(资源、方法、架构、错误、分页、版本控制)
- 步骤 4:根据客户端类型选择身份验证模型
- 步骤 5:按照契约实现框架层接口
- 步骤 6:验证行为和文档(若未通过,返回步骤 3 或步骤 5)
步骤 1:确定边界
在选择框架模式之前,先明确 API:
- 谁来调用? 浏览器应用、移动应用、第三方集成方、内部服务、Webhook 发送方
- 这是什么类型的 API? 公共 API、私有应用后端、内部服务、管理 API
- 这是从零开始的任务还是评审? 新设计、增量变更、迁移或 Bug 修复
- 稳定性承诺是什么? 仅供内部使用、带版本的公共 API,还是“尽力而为”
- 现有的身份验证模型是什么? Session Cookie、JWT Bearer、API 密钥、OAuth/OIDC,还是尚未设置
- 现有的项目规范是什么? 错误格式、分页形式、版本方案、身份验证边界、DTO 命名
如果用户要求“只添加一个端点”,请先检查周边服务。许多糟糕的 API 工作,都是因为某个路由采用了与服务其余部分不同的错误模型、身份验证规则、分页形式或 DTO 风格。
步骤 2:选择框架模式
选择符合代码库和团队约束的框架。不要为了赶时髦而更换框架。
| 框架 | 最适用场景 | 注意事项 |
|---|---|---|
| FastAPI | 使用类型化请求/响应模型且需要完善 OpenAPI 输出的 Python 服务 | 直接暴露 ORM 模型、混淆异步用法、将业务逻辑塞进路由函数 |
| Express | 精简的 Node.js 服务、自定义中间件栈、现有的成熟代码库 | 没有内置结构、验证逻辑散落在各个中间件中、错误处理不一致 |
| NestJS | 适合采用模块、守卫、管道、拦截器和依赖注入的大型 TypeScript 服务 | 过度抽象、装饰器过多导致间接层次繁杂、用过多层封装简单流程 |
步骤 3:先设计契约
编写处理器之前,先定义 API 契约:
对于现有服务的变更,除非任务明确包含迁移,否则应遵循当前的 URL、错误、分页和身份验证规范。
- 选择资源结构和 URI
- 选择方法和状态码
- 定义请求和响应架构
- 定义错误形式
- 确定分页、筛选、排序和版本规则
- 最后再接入框架实现
对于 GraphQL 或 gRPC 边界,在适用的情况下默认遵循 REST/HTTP 规则,然后阅读 references/graphql-grpc-patterns.md,了解协议专属的契约、兼容性、截止时间、授权、分页和流式传输保护措施。
优先采用:
- 基于名词的资源:
/users/{userId}/sessions - 可预测的列表端点:
GET /orders?cursor=...&limit=...&status=paid - 明确的写入语义:创建/命令使用
POST,完整替换使用PUT,部分更新使用PATCH,删除使用DELETE - 为
POST /orders/{orderId}/cancel等命令端点明确定义重试语义 - 整个服务采用统一的规范错误模型
阅读 references/http-api-patterns.md,了解方法语义、分页、幂等性和版本规则。
步骤 4:围绕客户端设计身份验证,而不是追逐 JWT 热潮
从客户端类型入手:
| 客户端 | 默认选择 | 原因 |
|---|---|---|
| 第一方浏览器应用 | 会话 Cookie 或 BFF/令牌中介后端 | 尽可能让令牌远离浏览器 |
| 原生/移动应用 | OAuth/OIDC 授权码 + PKCE | 当前公共客户端的标准方式 |
| 第三方集成方 | OAuth/OIDC;若 OAuth 过于复杂,则使用有作用域限制的 API 密钥 | 明确的授权委托和撤销机制 |
| 内部服务间通信 | 平台身份或短期服务凭据 | 避免服务之间使用面向用户的身份验证流程 |
身份验证规则:
- 对第一方 Web 应用,默认使用会话,除非有明确理由不这么做
- 只有客户端确实需要持有令牌时,才使用 Bearer 令牌
- 将 API 密钥视为机器凭据,而不是通用的身份验证捷径
- 在 API 边界定义作用域或角色边界,不要在随意的处理程序中临时定义
- 如果浏览器应用与第三方身份提供商通信,应遵循授权码 + PKCE 和当前的浏览器应用指南,而不是重新启用隐式授权流程模式
BFF 令牌中介(第一方浏览器应用调用外部 API):
Browser -> BFF (session cookie) -> BFF attaches Bearer token -> Upstream API
BFF 在服务端持有访问令牌;浏览器永远不会看到该令牌。
请阅读 references/auth-and-session-patterns.md,了解会话、Bearer 令牌、OAuth、BFF、刷新令牌和机器身份验证。
步骤 5:实现框架层
将契约转换为框架代码,但不要让框架决定契约。
FastAPI
- 为请求和响应定义 Pydantic 模型
- 使用依赖注入处理身份验证、数据库/会话获取和共享请求上下文
- 注册异常处理程序,确保输出符合 RFC 9457 且保持一致
- 保持路由处理程序精简;将业务规则移至服务或领域模块
Express
- 在控制器逻辑执行前验证输入
- 在中间件中附加身份验证信息和请求上下文
- 使用统一的错误处理中间件路径
- 控制器应作为传输适配器,而不是承载系统中所有规则的地方
NestJS
- 使用 DTO 类定义传输边界
- 在管道中进行验证,在守卫中处理身份验证
- 使用异常过滤器或拦截器,统一响应和错误行为
- 保持模块边界有意义;每个模糊概念都单独设一个模块并不等于架构
步骤 6:验证行为和文档
返回结果前,运行以下检查。如果有检查失败,请修复后返回步骤 3 处理契约缺陷,或返回步骤 5 处理实现缺陷,然后重新检查:
- 将 OpenAPI 文档与实际路由进行对照
- 验证各端点的身份验证要求
- 检查所有非 2xx 响应,而不只是成功路径
- 验证列表端点在空结果、部分结果和游标结束状态下的行为
- 检查创建/支付/Webhook 路径的重试和重复提交
- 检查命令端点(
cancel、resend、approve)重复调用时的行为,不要让重试行为含糊不清 - 将新端点与相邻端点进行对照,检查命名、DTO 结构、分页参数和错误一致性
- 将后续测试工作交由 testing,将后续安全审查交由 security-audit
契约防护原则
版本控制
- 优先采用增量式变更,而不是频繁变更版本
- 只有在需要进行破坏性变更时才升级版本
- 整个服务采用一种版本控制方案:对于公共 API,路径版本控制(
/v1/...)是最清晰的默认选择 - 不要在同一个 API 中混用路径版本控制、请求头版本控制和临时的
?version=查询参数
编写 OpenAPI
除非项目的工具链已证明能够端到端支持 3.2,否则请按 3.1 编写;参见 references/http-api-patterns.md 中的“OpenAPI 版本选择”。
错误模型
- 优先使用 RFC 9457 问题详情,包含稳定的
type、title、status和特定领域的扩展字段 - 不要让验证、身份验证和业务规则各自返回不同的错误结构
- 切勿向客户端泄露原始堆栈跟踪或 ORM 内部信息
最简 RFC 9457 问题详情响应:
{
"type": "https://api.example.com/errors/insufficient-funds",
"title": "Insufficient funds",
"status": 422,
"detail": "Account balance is $10.00; transfer requires $50.00."
}
状态码决策矩阵(选择最准确的状态码):
| 场景 | 代码 | 说明 |
|---|---|---|
| JSON 格式错误、缺少必填字段、类型错误 | 400 | 客户端请求语法有误 |
| 输入格式正确,但不符合领域规则(余额不足、状态无效) | 422 | 语义验证 |
| 没有凭据、令牌已过期 | 401 | 包含 WWW-Authenticate 标头 |
| 已通过身份验证,但未获授权 | 403 | 不要要求提供凭据 |
| 资源不存在,或资源存在但调用方不应知道 | 404 | 为每种资源选择一种策略并保持一致 |
| 写入与当前状态冲突(ETag 过期、唯一键重复) | 409 | 问题详情应指出冲突 |
| 使用不同的请求正文重用了 Idempotency-Key | 422 | 不是 409——密钥约定遭到违反 |
| 请求过多 | 429 | 包含 Retry-After |
| 未处理的服务器错误 | 500 | 响应正文中绝不泄露堆栈跟踪 |
FastAPI 异常处理程序配置,确保统一返回 problem+json:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=400,
media_type="application/problem+json",
content={
"type": "https://api.example.com/errors/validation",
"title": "Invalid request",
"status": 400,
"errors": [
{"location": [part for part in error.get("loc", ())
if isinstance(part, (str, int))],
"message": "Invalid value"}
for error in exc.errors()
],
},
)
分页和筛选
- 对于规模较小且稳定的后台列表,偏移分页即可
- 面向用户或写入频繁的集合默认使用游标分页
- 排序顺序必须稳定且有文档说明
- 筛选器名称应反映资源字段或清晰的领域概念,而非内部 SQL 列名
幂等性和重试
- 为客户端或网关可能重试的写入操作定义幂等性
- 对类似支付或容易重放请求的操作使用显式幂等键
- 存在异步工作流时,应区分“请求已接受”和“副作用已完成”
Express/NestJS 和 FastAPI 使用相同的事务协议;缓存的读取/写入/设置序列并非原子操作:
- 对操作进行身份验证和授权。限制标头长度,并将密钥限定到调用方或租户以及具体操作;对规范化后的请求进行哈希,生成指纹。
- 使用数据库唯一约束,以原子方式保留该作用域内的密钥。对于已有记录,若指纹不同则拒绝;若操作正在进行,则报告其状态,不要再次执行。仅当调用方和指纹都匹配时,才重放已完成且已授权的结果。
- 在同一事务中提交业务变更和已完成的响应记录。对于外部副作用,使用事务发件箱和服务提供方的幂等功能;数据库回滚无法撤销已发送的付款或消息。
- 在文档规定的重试窗口内保留结果。通过定义明确的状态转换来恢复中断的密钥保留操作,绝不要盲目地再次执行副作用。测试并发重复请求、两个调用方共享同一个密钥、请求正文不匹配,以及副作用与响应之间发生崩溃的情况。
游标分页响应封装:
{
"data": [...],
"next_cursor": "eyJpZCI6MTIzfQ",
"has_more": true
}
在服务端解码游标(WHERE id > :cursor_id ORDER BY id LIMIT :limit)。切勿在游标中暴露原始数据库偏移量或行号。
不透明游标的编码/解码(FastAPI,使用 HMAC 签名以防止客户端篡改):
import base64, hmac, hashlib, json, os
SECRET = os.environ["CURSOR_SECRET"].encode()
def encode_cursor(payload: dict) -> str:
body = base64.urlsafe_b64encode(json.dumps(payload, separators=(",", ":")).encode()).rstrip(b"=")
sig = base64.urlsafe_b64encode(hmac.new(SECRET, body, hashlib.sha256).digest()[:8]).rstrip(b"=")
return f"{body.decode()}.{sig.decode()}"
def decode_cursor(token: str) -> dict:
body, sig = token.split(".", 1)
expected = base64.urlsafe_b64encode(hmac.new(SECRET, body.encode(), hashlib.sha256).digest()[:8]).rstrip(b"=").decode()
if not hmac.compare_digest(sig, expected):
raise ValueError("invalid cursor")
return json.loads(base64.urlsafe_b64decode(body + "=="))
客户端将 next_cursor 视为不透明值;其结构由服务端控制,并可在不破坏契约的情况下更改。
优雅关闭和滚动部署
滚动部署会在新实例启动时向旧实例发送 SIGTERM。请在 API 端处理,否则负载较高时可能出现 502 错误:
- 捕获 SIGTERM,停止接受新连接,排空正在处理的请求,然后退出。FastAPI/Uvicorn:设置
--timeout-graceful-shutdown(默认未设置,因此 Uvicorn 会等待正在处理的请求完成且不设超时;生产环境中应设置有界值);Express 5:调用server.close(),然后在排空窗口结束后调用server.closeAllConnections();NestJS:调用app.enableShutdownHooks(),并设置onApplicationShutdown处理程序 - 确保应用的关闭窗口短于编排器的终止宽限期(Kubernetes 默认值为 30s)。
app_shutdown < terminationGracePeriodSeconds,否则内核会杀死正在处理的工作 - 将服务器 HTTP keep-alive 超时设置得长于上游后端连接的空闲超时(负载均衡器、入口)。如果 LB 保留的连接已被服务器关闭,下一个请求就会命中失效的套接字。常见的安全搭配:服务器 keep-alive 为 65s,位于空闲超时为 60s 的 LB 后方
- 添加就绪探针,在排空开始前于收到 SIGTERM 时切换为失败状态。编排器会停止路由新流量,同时让正在处理的请求完成
不要强行采用的做法
- 不要强行要求每个小型内部列表或后台管理表格都使用游标分页
- 不要仅仅因为添加了新字段就切出
/v2 - 如果范围限定的 API 密钥或平台身份更合适,就不要坚持要求每条内部自动化路径都使用 OAuth
- 不要因为“JWT auth”听起来更现代,就让一方浏览器应用采用将 bearer token 存储在本地存储中的模式
- 不要把简单的 Express 服务改造成带有多余层和装饰器的伪企业级架构
参考文件
references/http-api-patterns.md- 资源设计、方法语义、状态码、版本控制、分页、筛选、幂等性references/auth-and-session-patterns.md- 会话与 bearer token、OAuth/OIDC、BFF、刷新令牌、机器客户端references/graphql-grpc-patterns.md- 有界 GraphQL 和 gRPC 契约及兼容性模式
输出契约
完整契约请参见 references/output-contract.md。
- 技能名称: BACKEND-API
- 交付物类别:
audits - 模式: 条件式。调用以分析、审查、审计或改进现有仓库内容时,遵循
references/output-contract.md中的报告篇幅和证据规则,并将交付物写入docs/local/audits/backend-api/<YYYY-MM-DD>-<slug>.md。调用以回答问题、讲解概念、构建新产物或生成内容时,可自由作答,不受该契约约束。 - 严重性级别:
P0 | P1 | P2 | P3 | info(参见共享契约;仅用于审计/审查模式)。
相关技能
- databases - API 背后的架构设计、查询调优、迁移和持久化问题
- code-review - 正确性缺陷、逻辑错误及与 API 无关的审查发现
- security-audit - 认证绕过、OWASP 问题、机密,以及 API 设计完成后的安全审查
- testing - 路由、集成、契约和端到端验证
- docker - 将 API 服务容器化
- kubernetes - 在集群上部署 API 服务
- networking - 反向代理、TLS 终止、与 CORS 相关的网络边界及网关行为
- ci-cd - API 服务的交付流水线
- mcp - MCP 专用 HTTP 服务和认证模式
- message-queues - 通过
202 Accepted端点进行经由代理的异步交付、端点后方的队列,以及通过代理进行 webhook 扇出
规则
- 先定义契约,再编写处理程序。 在编写路由代码之前,先设计资源模型、架构、错误和身份验证边界。
- 不要让传输层泄漏持久化细节。 数据库表、ORM 实体和内部枚举都不是公共 API 契约。
- 保持框架结构简单明了。 控制器或路由保持精简,明确执行验证和身份验证,并集中处理错误。
- 向后兼容是一项特性。 新增字段成本低;破坏客户端兼容性代价高。
- 为真实的重试场景而设计。 假设客户端、代理和作业运行器会重放请求。
- 应用特定协议规则。 默认重点关注 REST;GraphQL 和 gRPC 需要遵循链接参考中的专属架构、兼容性、截止时间和流式传输检查。