backend-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Backend APIs: HTTP Service Design and Implementation

后端API:HTTP服务设计与实现

Design and review HTTP APIs that stay coherent as they grow. Focus on contracts, auth boundaries, error models, and framework structure for Python and Node.js services.
Target versions (April 2026):
  • FastAPI 0.135.3 (released 2026-04-01)
  • Express 5.2.1 (published 2025-12-01)
  • NestJS 11.1.18 (published 2026-04-03)
  • OpenAPI Specification 3.2.0 (published 2025-09-19)
  • HTTP Semantics: RFC 9110 (June 2022)
  • Problem Details for HTTP APIs: RFC 9457 (July 2023)
  • OAuth 2.0 Security Best Current Practice: RFC 9700 (January 2025)
This skill works across five concerns:
  • Contract design - resources, methods, status codes, schemas, versioning
  • API ergonomics - pagination, filtering, sorting, idempotency, error format
  • Framework structure - FastAPI dependencies, Express middleware, NestJS modules and guards
  • Authentication - sessions, bearer tokens, OAuth/OIDC, BFF and token mediation
  • Review - unstable contracts, DTO leakage, auth confusion, and HTTP misuse
设计和评审可随规模增长保持一致性的HTTP API。聚焦于Python和Node.js服务的契约、认证边界、错误模型及框架结构。
目标版本(2026年4月)
  • FastAPI 0.135.3(发布于2026-04-01)
  • Express 5.2.1(发布于2025-12-01)
  • NestJS 11.1.18(发布于2026-04-03)
  • OpenAPI Specification 3.2.0(发布于2025-09-19)
  • HTTP语义:RFC 9110(2022年6月)
  • HTTP API问题详情:RFC 9457(2023年7月)
  • OAuth 2.0安全最佳实践:RFC 9700(2025年1月)
本技能覆盖五大核心关注点:
  • 契约设计 - 资源、方法、状态码、Schema、版本控制
  • API易用性 - 分页、过滤、排序、幂等性、错误格式
  • 框架结构 - FastAPI依赖、Express中间件、NestJS模块与守卫
  • 认证机制 - 会话、Bearer令牌、OAuth/OIDC、BFF与令牌中转
  • 评审要点 - 不稳定契约、DTO泄露、认证混淆、HTTP误用

When to use

适用场景

  • Designing a new REST or HTTP API
  • Reviewing an existing FastAPI, Express, or NestJS service
  • Writing or fixing OpenAPI specifications and generated docs
  • Choosing status codes, error formats, pagination, filtering, or versioning strategy
  • Designing auth flows for browser, mobile, machine-to-machine, or third-party clients
  • Refactoring route or controller structure that has become hard to reason about
  • Planning backward-compatible API evolution
  • Defining idempotency and retry behavior for write endpoints
  • 设计新的REST或HTTP API
  • 评审现有FastAPI、Express或NestJS服务
  • 编写或修复OpenAPI规范及生成文档
  • 选择状态码、错误格式、分页、过滤或版本控制策略
  • 为浏览器、移动端、机器对机器或第三方客户端设计认证流程
  • 重构难以理解的路由或控制器结构
  • 规划向后兼容的API演进方案
  • 定义写入端点的幂等性与重试行为

When NOT to use

不适用场景

  • GraphQL schema, resolvers, federation, or persisted query work - out of scope for this skill
  • gRPC, protobuf schema evolution, or streaming RPCs - out of scope for this skill
  • Database schema, indexing, replication, or query tuning - use databases
  • General bug-finding, race-condition hunting, or correctness review outside the API boundary - use code-review
  • Security auditing for auth bypasses, injection, secrets, or OWASP findings - use security-audit
  • Writing or debugging automated tests - use testing
  • Deployment, containers, gateways, ingress, or cluster config - use docker, kubernetes, or networking
  • CI/CD pipeline design for API delivery - use ci-cd
  • MCP-specific HTTP servers and tool handlers - use mcp

  • GraphQL Schema、解析器、联邦或持久化查询工作 - 超出本技能范围
  • gRPC、Protobuf Schema演进或流式RPC - 超出本技能范围
  • 数据库Schema、索引、复制或查询调优 - 使用databases技能
  • API边界外的通用bug排查、竞态条件检测或正确性评审 - 使用code-review技能
  • 认证绕过、注入攻击、密钥泄露或OWASP漏洞的安全审计 - 使用security-audit技能
  • 编写或调试自动化测试 - 使用testing技能
  • 部署、容器、网关、入口或集群配置 - 使用dockerkubernetesnetworking技能
  • API交付的CI/CD流水线设计 - 使用ci-cd技能
  • MCP专属HTTP服务器与工具处理器 - 使用mcp技能

AI Self-Check

AI自检清单

Before returning API code, route design, or OpenAPI output, verify:
  • Resource names are nouns and URL shape is stable (
    /users/{userId}/sessions
    , not
    /doUserSessionThing
    )
  • Method semantics follow RFC 9110 - no "GET that mutates", no PATCH used as a vague catch-all
  • 401
    vs
    403
    is correct: unauthenticated vs authenticated-but-forbidden
  • Ownership-hiding behavior is deliberate and consistent: decide when out-of-scope resources return
    404
    vs
    403
  • Error responses use one consistent format, preferably RFC 9457 problem details
  • Request and response DTOs are explicit and separate from ORM or database models
  • Input validation happens server-side even if clients or SDKs also validate
  • Offset pagination is not used blindly on large or high-churn collections where cursor pagination is the safer default
  • Write endpoints that may be retried (
    POST /payments
    , webhook receivers, order creation) define idempotency behavior
  • OpenAPI docs match real handler behavior, examples, status codes, and auth requirements
  • First-party browser apps do not get long-lived bearer tokens stored in browser storage by default
  • Cookie-based browser auth accounts for cookie scope and CSRF behavior instead of assuming cookies are automatically safe
  • OAuth guidance is current: authorization code + PKCE, no implicit flow, no resource owner password credentials
  • Sensitive defaults are explicit: cookie flags, token TTLs, scope boundaries, and rate limits are not hand-waved

返回API代码、路由设计或OpenAPI输出前,请验证:
  • 资源名称为名词且URL结构稳定(如
    /users/{userId}/sessions
    ,而非
    /doUserSessionThing
  • 方法语义遵循RFC 9110 - 禁止“会修改数据的GET”,禁止将PATCH用作模糊的万能方法
  • 401
    403
    区分正确:未认证 vs 已认证但无权限
  • 隐藏资源所有权的行为是刻意且一致的:明确决定超出范围的资源返回
    404
    还是
    403
  • 错误响应使用统一格式,优先采用RFC 9457问题详情
  • 请求和响应DTO明确且与ORM或数据库模型分离
  • 即使客户端或SDK已做验证,服务器端仍需执行输入验证
  • 不会盲目在大型或高变动集合上使用偏移分页,游标分页是更安全的默认选择
  • 可能被重试的写入端点(如
    POST /payments
    、Webhook接收器、订单创建)需定义幂等性行为
  • OpenAPI文档与实际处理器行为、示例、状态码及认证要求一致
  • 默认情况下,第一方浏览器应用不会将长期有效的Bearer令牌存储在浏览器存储中
  • 基于Cookie的浏览器认证需考虑Cookie作用域和CSRF行为,而非默认认为Cookie是自动安全的
  • OAuth指南符合当前标准:授权码+PKCE,禁止隐式流,禁止资源所有者密码凭证流
  • 敏感默认值明确:Cookie标志、令牌TTL、权限范围边界和速率限制需明确配置,而非模糊处理

Workflow

工作流程

Build vs. Review: when reviewing an existing service, still walk the same steps. Determine the API boundary, audit the contract, trace auth, then compare the implementation against the published behavior instead of jumping straight into handler code.
构建 vs 评审:评审现有服务时,仍遵循相同步骤。先确定API边界,审计契约,追踪认证流程,再将实现与发布的行为进行对比,而非直接查看处理器代码。

Step 1: Determine the boundary

步骤1:确定边界

Clarify the API before picking framework patterns:
  • Who calls it? Browser app, mobile app, third-party integrator, internal service, webhook sender
  • What kind of API is it? Public API, private app backend, internal service, admin API
  • Is the task greenfield or review? New design, incremental change, migration, or bug fix
  • What stability promise exists? Internal-only, versioned public API, or "best effort"
  • What auth model already exists? Session cookies, JWT bearer, API keys, OAuth/OIDC, or none yet
  • What house style already exists? Error format, pagination shape, versioning scheme, auth boundary, DTO naming
If the user asks to "just add an endpoint", inspect the surrounding service first. Most bad API work happens when one route lands with a different error model, auth rule, pagination shape, or DTO style than the rest of the service.
在选择框架模式前,先明确API的核心信息:
  • 调用方是谁? 浏览器应用、移动端应用、第三方集成商、内部服务、Webhook发送方
  • API类型是什么? 公开API、私有应用后端、内部服务、管理API
  • 任务是全新设计还是评审? 新设计、增量变更、迁移或bug修复
  • 稳定性承诺是什么? 仅内部使用、版本化公开API或“尽力而为”
  • 已有的认证模型是什么? 会话Cookie、JWT Bearer、API密钥、OAuth/OIDC或尚未配置
  • 已有的团队规范是什么? 错误格式、分页结构、版本控制方案、认证边界、DTO命名规则
如果用户要求“仅添加一个端点”,请先检查周边服务。大多数糟糕的API设计都源于单个路由的错误模型、认证规则、分页结构或DTO风格与服务其余部分不一致。

Step 2: Choose the framework pattern

步骤2:选择框架模式

Pick the framework that matches the codebase and team constraints. Do not switch frameworks for fashion.
FrameworkBest fitWatch for
FastAPIPython services with typed request/response models and strong OpenAPI outputLeaking ORM models directly, async confusion, piling business logic into route functions
ExpressThin Node.js services, custom middleware stacks, existing mature codebasesNo built-in structure, validation scattered across middleware, inconsistent error handling
NestJSLarger TypeScript services that benefit from modules, guards, pipes, interceptors, and DIOver-abstraction, decorator-heavy indirection, hiding simple flows behind too many layers
Framework rules:
  • FastAPI - keep dependencies explicit, use response models, and centralize exception handling
  • Express - keep routing, validation, auth, and business logic separated; add one error middleware path
  • NestJS - keep controllers thin, move auth into guards, validation into pipes, and cross-cutting behavior into interceptors or filters
选择符合代码库和团队约束的框架,不要为了跟风切换框架。
框架最佳适用场景注意事项
FastAPI带有类型化请求/响应模型且需要强大OpenAPI输出的Python服务避免直接泄露ORM模型、异步逻辑混淆、将业务逻辑堆积到路由函数中
Express轻量Node.js服务、自定义中间件栈、成熟的现有代码库无内置结构、验证逻辑分散在中间件中、错误处理不一致
NestJS可从模块、守卫、管道、拦截器和DI中受益的大型TypeScript服务过度抽象、装饰器导致的间接性过强、将简单流程隐藏在过多层级后
框架规则:
  • FastAPI - 保持依赖明确,使用响应模型,集中处理异常
  • Express - 分离路由、验证、认证和业务逻辑;统一错误中间件路径
  • NestJS - 保持控制器轻量化,将认证逻辑移至守卫,验证逻辑移至管道,横切行为移至拦截器或过滤器

Step 3: Design the contract first

步骤3:先设计契约

Define the API contract before writing handlers:
For changes to an existing service, match the current URL, error, pagination, and auth house style unless the task explicitly includes a migration.
  1. Pick resource shapes and URIs
  2. Choose methods and status codes
  3. Define request and response schemas
  4. Define error shapes
  5. Decide pagination, filtering, sorting, and versioning rules
  6. Only then wire the framework implementation
Prefer:
  • Noun-based resources:
    /users/{userId}/sessions
  • Predictable list endpoints:
    GET /orders?cursor=...&limit=...&status=paid
  • Explicit write semantics:
    POST
    for create/commands,
    PUT
    for full replacement,
    PATCH
    for partial update,
    DELETE
    for delete
  • Explicit retry semantics for command endpoints like
    POST /orders/{orderId}/cancel
  • One canonical error model across the whole service
Read
references/http-api-patterns.md
for method semantics, pagination, idempotency, and versioning rules.
在编写处理器前定义API契约:
对于现有服务的变更,需匹配当前的URL、错误、分页和认证团队规范,除非任务明确包含迁移要求。
  1. 确定资源结构和URI
  2. 选择方法和状态码
  3. 定义请求和响应Schema
  4. 定义错误结构
  5. 确定分页、过滤、排序和版本控制规则
  6. 最后再对接框架实现
优先选择:
  • 基于名词的资源:
    /users/{userId}/sessions
  • 可预测的列表端点:
    GET /orders?cursor=...&limit=...&status=paid
  • 明确的写入语义:
    POST
    用于创建/命令,
    PUT
    用于全量替换,
    PATCH
    用于部分更新,
    DELETE
    用于删除
  • 命令端点(如
    POST /orders/{orderId}/cancel
    )的明确重试语义
  • 整个服务使用统一的错误模型
阅读
references/http-api-patterns.md
获取方法语义、分页、幂等性和版本控制规则。

Step 4: Design auth around the client, not around JWT hype

步骤4:围绕客户端设计认证,而非跟风JWT

Start from the client type:
ClientDefault choiceWhy
First-party browser appSession cookie or BFF/token-mediating backendKeeps tokens off the browser as much as possible
Native/mobile appOAuth/OIDC authorization code + PKCECurrent standard path for public clients
Third-party integratorOAuth/OIDC or scoped API keys if OAuth is overkillExplicit delegation and revocation story
Internal service-to-servicePlatform identity or short-lived service credentialsAvoid user-style auth flows between services
Auth rules:
  • Use sessions by default for first-party web apps unless there is a clear reason not to
  • Use bearer tokens when the client really is a token-holding client
  • Treat API keys as machine credentials, not as a universal auth shortcut
  • Define scope or role boundaries at the API boundary, not ad hoc inside random handlers
  • If the browser app talks to third-party identity providers, follow authorization code + PKCE and current browser-app guidance rather than resurrecting implicit flow patterns
BFF token mediation (first-party browser app calling an external API):
Browser -> BFF (session cookie) -> BFF attaches Bearer token -> Upstream API
The BFF holds the access token server-side; the browser never sees it.
Read
references/auth-and-session-patterns.md
for sessions, bearer tokens, OAuth, BFF, refresh tokens, and machine auth.
从客户端类型出发:
客户端默认选择原因
第一方浏览器应用会话Cookie或BFF/令牌中转后端尽可能避免令牌暴露在浏览器中
原生/移动应用OAuth/OIDC授权码+PKCE公共客户端的当前标准方案
第三方集成商OAuth/OIDC或范围化API密钥(若OAuth过于复杂)明确的委托和撤销机制
内部服务间调用平台身份或短期服务凭证避免服务间使用用户风格的认证流程
认证规则:
  • 第一方Web应用默认使用会话,除非有明确的理由不使用
  • 仅当客户端确实需要持有令牌时才使用Bearer令牌
  • 将API密钥视为机器凭证,而非通用认证捷径
  • 在API边界定义权限范围或角色边界,而非在随机处理器中临时配置
  • 如果浏览器应用对接第三方身份提供商,遵循授权码+PKCE和当前浏览器应用指南,而非复用隐式流模式
BFF令牌中转(第一方浏览器应用调用外部API):
浏览器 -> BFF(会话Cookie) -> BFF附加Bearer令牌 -> 上游API
BFF在服务器端存储访问令牌;浏览器永远不会接触到令牌。
阅读
references/auth-and-session-patterns.md
获取会话、Bearer令牌、OAuth、BFF、刷新令牌和机器认证的相关内容。

Step 5: Implement the framework surface

步骤5:实现框架层面的代码

Convert the contract into framework code without letting the framework dictate the contract.
FastAPI
  • Define Pydantic models for requests and responses
  • Use dependency injection for auth, DB/session acquisition, and shared request context
  • Register exception handlers for consistent RFC 9457 output
  • Keep route handlers thin; move business rules into services or domain modules
Express
  • Validate inputs before controller logic
  • Attach auth and request context in middleware
  • Use one shared error-handling middleware path
  • Keep controllers as transport adapters, not the place where every rule in the system lives
NestJS
  • Use DTO classes for transport boundaries
  • Put validation in pipes and auth in guards
  • Use exception filters or interceptors to normalize response and error behavior
  • Keep module boundaries meaningful; one module per vague concept is not architecture
将契约转换为框架代码,避免让框架主导契约设计。
FastAPI
  • 为请求和响应定义Pydantic模型
  • 使用依赖注入处理认证、数据库/会话获取和共享请求上下文
  • 注册异常处理器以输出一致的RFC 9457格式
  • 保持路由处理器轻量化;将业务规则移至服务或领域模块
Express
  • 在控制器逻辑前验证输入
  • 在中间件中附加认证和请求上下文
  • 使用统一的错误处理中间件路径
  • 将控制器作为传输适配器,而非存放所有系统规则的地方
NestJS
  • 使用DTO类处理传输边界
  • 将验证逻辑放在管道中,认证逻辑放在守卫中
  • 使用异常过滤器或拦截器标准化响应和错误行为
  • 保持模块边界有意义;每个模糊概念对应一个模块并非合理架构

Step 6: Validate behavior and docs

步骤6:验证行为与文档

Before returning the result:
  • Compare OpenAPI docs against the real routes
  • Verify auth requirements per endpoint
  • Check all non-2xx responses, not just the happy path
  • Verify list endpoints under empty, partial, and end-of-cursor states
  • Check retries and duplicate submissions for create/payment/webhook paths
  • Check repeat-call behavior for command endpoints (
    cancel
    ,
    resend
    ,
    approve
    ) instead of leaving retries implicit
  • Compare new endpoints with neighboring endpoints for naming, DTO shape, pagination params, and error consistency
  • Route follow-up testing work to testing and follow-up security review to security-audit

返回结果前:
  • 对比OpenAPI文档与实际路由
  • 验证每个端点的认证要求
  • 检查所有非2xx响应,而非仅关注正常路径
  • 验证列表端点在空数据、部分数据和游标结束状态下的表现
  • 检查创建/支付/Webhook路径的重试和重复提交行为
  • 检查命令端点(
    cancel
    resend
    approve
    )的重复调用行为,而非隐式处理重试
  • 对比新端点与相邻端点的命名、DTO结构、分页参数和错误一致性
  • 将后续测试工作交给testing技能,后续安全评审交给security-audit技能

Contract Guardrails

契约约束规则

Versioning

版本控制

  • Prefer additive change over version churn
  • Version only when you need a breaking change
  • Keep one versioning scheme for the service: path versioning (
    /v1/...
    ) is the clearest default for public APIs
  • Do not mix path versioning, header versioning, and ad hoc
    ?version=
    query params in one API
  • 优先选择增量变更而非频繁版本迭代
  • 仅在需要破坏性变更时才进行版本控制
  • 服务使用统一的版本控制方案:路径版本控制(
    /v1/...
    )是公开API最清晰的默认选择
  • 不要在同一个API中混合使用路径版本控制、头部版本控制和临时的
    ?version=
    查询参数

OpenAPI authoring

OpenAPI编写

  • OpenAPI
    3.2.0
    is the current spec, but much of the framework and Swagger ecosystem still centers on
    3.1.x
  • Default to authoring for
    3.1
    compatibility unless the actual toolchain in the project proves
    3.2
    support end-to-end
  • Do not advertise
    3.2
    features in generated specs just because the top-level standard moved
  • OpenAPI
    3.2.0
    是当前规范,但多数框架和Swagger生态仍以
    3.1.x
    为核心
  • 默认编写兼容
    3.1
    的规范,除非项目中的实际工具链证明完全支持
    3.2
  • 不要仅因为顶层标准更新就在生成的规范中宣传
    3.2
    特性

Error model

错误模型

  • Prefer RFC 9457 problem details with stable
    type
    ,
    title
    ,
    status
    , and domain-specific extension fields
  • Do not return one error shape from validation, another from auth, and a third from business rules
  • Never leak raw stack traces or ORM internals to clients
Minimal RFC 9457 problem detail response:
json
{
  "type": "https://api.example.com/errors/insufficient-funds",
  "title": "Insufficient funds",
  "status": 422,
  "detail": "Account balance is $10.00; transfer requires $50.00."
}
Status code decision matrix (pick the narrowest correct code):
ScenarioCodeNotes
Malformed JSON, missing required field, wrong type
400
Client request is syntactically wrong
Well-formed input but fails domain rule (insufficient funds, invalid state)
422
Semantic validation
No credentials, expired token
401
Include
WWW-Authenticate
header
Authenticated but not permitted
403
Do not challenge for credentials
Resource does not exist, or exists but caller must not know
404
Pick one policy per resource and keep it
Write conflicts with current state (stale ETag, duplicate unique key)
409
Problem detail should name the conflict
Idempotency-Key reused with a different body
422
Not
409
- the key contract is broken
Too many requests
429
Include
Retry-After
Unhandled server error
500
Never leak stack traces in the body
FastAPI exception handler wiring for consistent problem+json:
python
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": exc.errors(),
        },
    )
  • 优先采用带有稳定
    type
    title
    status
    和领域扩展字段的RFC 9457问题详情
  • 不要在验证、认证和业务规则中返回不同的错误结构
  • 永远不要向客户端泄露原始堆栈跟踪或ORM内部细节
最小化RFC 9457问题详情响应:
json
{
  "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格式):
python
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": exc.errors(),
        },
    )

Pagination and filtering

分页与过滤

  • Offset pagination is fine for small, stable backoffice lists
  • Cursor pagination is the default for user-facing or high-write collections
  • Sort order must be stable and documented
  • Filter names should reflect resource fields or clear domain concepts, not internal SQL column names
  • 偏移分页适用于小型、稳定的后台列表
  • 游标分页是面向用户或高写入集合的默认选择
  • 排序顺序必须稳定且有文档说明
  • 过滤名称应反映资源字段或清晰的领域概念,而非内部SQL列名

Idempotency and retries

幂等性与重试

  • Define idempotency for writes that clients or gateways may retry
  • Use explicit idempotency keys for payment-like or request-replay-prone operations
  • Distinguish "request accepted" from "side effect completed" when async workflows exist
Idempotency key header pattern (Express/NestJS):
typescript
const key = req.headers['idempotency-key'];
if (key) {
  const cached = await cache.get(`idem:${key}`);
  if (cached) return res.status(cached.status).json(cached.body);
}
// ... execute, then store result keyed by idempotency-key before returning
Idempotency key header pattern (FastAPI):
python
from fastapi import Header, HTTPException

async def create_order(
    body: OrderCreate,
    idempotency_key: str | None = Header(default=None, alias="Idempotency-Key"),
):
    if idempotency_key:
        cached = await cache.get(f"idem:{idempotency_key}")
        if cached:
            return JSONResponse(status_code=cached["status"], content=cached["body"])
    result = await orders.create(body)
    if idempotency_key:
        await cache.set(f"idem:{idempotency_key}", {"status": 201, "body": result}, ttl=86400)
    return result
Cursor pagination response envelope:
json
{
  "data": [...],
  "next_cursor": "eyJpZCI6MTIzfQ",
  "has_more": true
}
Decode the cursor server-side (
WHERE id > :cursor_id ORDER BY id LIMIT :limit
). Never expose raw DB offsets or row numbers in the cursor.
Opaque cursor encode/decode (FastAPI, HMAC-signed to prevent client tampering):
python
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 + "=="))
Clients treat
next_cursor
as opaque; the server controls shape and can change it without a breaking contract.
  • 为客户端或网关可能重试的写入操作定义幂等性
  • 对于类似支付或易重放的请求,使用明确的幂等性密钥
  • 异步工作流存在时,区分“请求已接受”和“副作用已完成”
幂等性密钥头部模式(Express/NestJS):
typescript
const key = req.headers['idempotency-key'];
if (key) {
  const cached = await cache.get(`idem:${key}`);
  if (cached) return res.status(cached.status).json(cached.body);
}
// ... 执行操作,然后在返回前将结果按幂等性密钥存储
幂等性密钥头部模式(FastAPI):
python
from fastapi import Header, HTTPException

async def create_order(
    body: OrderCreate,
    idempotency_key: str | None = Header(default=None, alias="Idempotency-Key"),
):
    if idempotency_key:
        cached = await cache.get(f"idem:{idempotency_key}")
        if cached:
            return JSONResponse(status_code=cached["status"], content=cached["body"])
    result = await orders.create(body)
    if idempotency_key:
        await cache.set(f"idem:{idempotency_key}", {"status": 201, "body": result}, ttl=86400)
    return result
游标分页响应结构:
json
{
  "data": [...],
  "next_cursor": "eyJpZCI6MTIzfQ",
  "has_more": true
}
服务器端解码游标(
WHERE id > :cursor_id ORDER BY id LIMIT :limit
)。永远不要在游标中暴露原始数据库偏移量或行号。
不透明游标编解码(FastAPI,使用HMAC签名防止客户端篡改):
python
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
视为不透明字符串;服务器控制其结构,可在不破坏契约的情况下修改。

Graceful shutdown and rolling deploys

优雅停机与滚动部署

Rolling deploys send SIGTERM to old instances while new ones come up. Handle it on the API side or expect 502s under load:
  • Trap SIGTERM, stop accepting new connections, drain in-flight requests, then exit. FastAPI/Uvicorn: configure
    --timeout-graceful-shutdown
    (default 30s); Express 5:
    server.close()
    then
    server.closeAllConnections()
    after the drain window; NestJS:
    app.enableShutdownHooks()
    plus
    onApplicationShutdown
    handlers
  • Keep the app-side shutdown window shorter than the orchestrator's termination grace (Kubernetes default 30s).
    app_shutdown < terminationGracePeriodSeconds
    or the kernel kills in-flight work
  • Set HTTP keep-alive timeout shorter than any upstream idle timeout (load balancer, ingress). If the LB holds a connection the server already closed, the next request hits a dead socket. Typical safe pair: server keep-alive 65s behind an LB with 60s idle
  • Add a readiness probe that flips to failing on SIGTERM before the drain starts. The orchestrator stops routing new traffic while in-flight requests finish
滚动部署会向旧实例发送SIGTERM信号,同时启动新实例。需在API层面处理该信号,否则高负载下会出现502错误:
  • 捕获SIGTERM,停止接受新连接,处理完正在进行的请求后再退出。FastAPI/Uvicorn:配置
    --timeout-graceful-shutdown
    (默认30秒);Express 5:调用
    server.close()
    后,在等待窗口结束时调用
    server.closeAllConnections()
    ;NestJS:启用
    app.enableShutdownHooks()
    并添加
    onApplicationShutdown
    处理器
  • 应用层面的停机窗口需短于编排器的终止宽限期(Kubernetes默认30秒)。
    app_shutdown < terminationGracePeriodSeconds
    ,否则内核会终止正在进行的工作
  • 将HTTP长连接超时设置为短于上游空闲超时(负载均衡器、入口)。如果负载均衡器持有已被服务器关闭的连接,下一次请求会命中无效套接字。典型安全配置:服务器长连接超时65秒,上游负载均衡器空闲超时60秒
  • 添加就绪探针,在SIGTERM信号触发后、开始处理请求前将探针状态设为失败。编排器会停止向实例路由新流量,同时允许正在进行的请求完成

What NOT to Force

不要强制推行的做法

  • Do not force cursor pagination onto every small internal list or backoffice table
  • Do not cut
    /v2
    just because a new field got added
  • Do not insist on OAuth for every internal automation path if scoped API keys or platform identity fit better
  • Do not push first-party browser apps toward bearer-token-in-local-storage patterns because "JWT auth" sounds modern
  • Do not turn simple Express services into pseudo-enterprise architectures with needless layers and decorators

  • 不要强制每个小型内部列表或后台表格都使用游标分页
  • 不要仅仅因为新增了一个字段就推出
    /v2
    版本
  • 如果范围化API密钥或平台身份更合适,不要强制每个内部自动化路径都使用OAuth
  • 不要因为“JWT认证”听起来更现代,就推动第一方浏览器应用采用“本地存储Bearer令牌”的模式
  • 不要将简单的Express服务强制改为带有不必要层级和装饰器的伪企业架构

Reference Files

参考文件

  • references/http-api-patterns.md
    - resource design, method semantics, status codes, versioning, pagination, filtering, idempotency
  • references/auth-and-session-patterns.md
    - sessions vs bearer tokens, OAuth/OIDC, BFF, refresh tokens, machine clients
  • references/http-api-patterns.md
    - 资源设计、方法语义、状态码、版本控制、分页、过滤、幂等性
  • references/auth-and-session-patterns.md
    - 会话vs Bearer令牌、OAuth/OIDC、BFF、刷新令牌、机器客户端

Related Skills

相关技能

  • databases - schema design, query tuning, migrations, and persistence concerns behind the API
  • code-review - correctness bugs, logic errors, and non-API-specific review findings
  • security-audit - auth bypasses, OWASP findings, secrets, and security review after the API design exists
  • testing - route, integration, contract, and end-to-end verification
  • docker - containerizing API services
  • kubernetes - deploying API services on clusters
  • networking - reverse proxies, TLS termination, CORS-adjacent network boundaries, and gateway behavior
  • ci-cd - delivery pipelines for API services
  • mcp - MCP-specific HTTP servers and auth patterns
  • databases - API背后的Schema设计、查询调优、迁移和持久化关注点
  • code-review - 正确性bug、逻辑错误和非API专属的评审发现
  • security-audit - API设计完成后的认证绕过、OWASP漏洞、密钥泄露和安全评审
  • testing - 路由、集成、契约和端到端验证
  • docker - 将API服务容器化
  • kubernetes - 在集群上部署API服务
  • networking - 反向代理、TLS终止、CORS相关网络边界和网关行为
  • ci-cd - API服务的交付流水线
  • mcp - MCP专属HTTP服务器和认证模式

Rules

规则

  1. Contract before handlers. Design the resource model, schemas, errors, and auth boundary before writing route code.
  2. Do not let transport leak persistence. Database tables, ORM entities, and internal enums are not public API contracts.
  3. Pick one error format. Prefer RFC 9457 and apply it consistently.
  4. Use current auth guidance. Authorization code + PKCE for public OAuth clients, sessions or BFF patterns for first-party browser apps, no implicit flow, no password grant.
  5. Keep framework structure boring. Thin controllers or routes, explicit validation, explicit auth, centralized error handling.
  6. Backward compatibility is a feature. New fields are cheap; breaking clients is expensive.
  7. Write for real retries. Assume clients, proxies, and job runners will replay requests.
  8. Keep scope on HTTP APIs. When the problem is GraphQL or gRPC-specific, say so and stop pretending the same rules apply.
  9. Match service conventions unless migrating them. A one-off endpoint with a different error, auth, or pagination model is usually a contract bug.
  1. 先契约后处理器。在编写路由代码前,先设计资源模型、Schema、错误和认证边界。
  2. 不要让传输层泄露持久化细节。数据库表、ORM实体和内部枚举不是公开API契约。
  3. 选择一种错误格式。优先采用RFC 9457并保持一致。
  4. 使用当前的认证指南。公共OAuth客户端使用授权码+PKCE,第一方浏览器应用使用会话或BFF模式,禁止隐式流和密码授权。
  5. 保持框架结构简洁。轻量化控制器或路由、明确的验证、明确的认证、集中式错误处理。
  6. 向后兼容性是一项特性。新增字段成本低;破坏客户端兼容性成本高。
  7. 为实际重试场景设计。假设客户端、代理和任务运行器会重放请求。
  8. 聚焦HTTP API。当问题是GraphQL或gRPC专属时,明确说明并停止套用相同规则。
  9. 匹配服务规范除非进行迁移。与现有错误、认证或分页模型不一致的孤立端点通常是契约bug。