mcpField
R
rest-api-design

rest-api-design

github.com/bm629开发6107 次安装1 周前更新
代码仓库

关于 rest-api-design

rest-api-design 是一项用于设计 REST/HTTP API 资源、行为和契约的技能,面向需要建立一致、可演进 API 的工程师。它提供状态码、错误格式、分页、版本与安全等设计规范,并将设计映射为 OpenAPI 3.1 契约。

主要功能

  1. 01按资源名词设计一致的 URL
  2. 02选择 HTTP 方法、状态码与幂等策略
  3. 03采用 RFC 9457 错误模型与统一分页
  4. 04制定版本、认证和限流方案
  5. 05将设计映射到 OpenAPI 3.1

适用场景

  1. 01从领域实体规划新的 REST API
  2. 02统一已有 API 的错误和分页格式
  3. 03确定请求结果对应的状态码
  4. 04把已定 API 设计整理成 OpenAPI 契约

安装

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

$npx skills add https://github.com/bm629/agent-skills --skill rest-api-design

安装后在 Claude Code 中输入/rest-api-design确认已加载。

包含的文件

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

references/
contract-patterns.md
error-model.md
openapi-mapping.md
sources.md

详细介绍

rest-api-design — SKILL.md

变体: standard · 使用时机: 调用此技能时,它会将以下设计决策应用于资源界面,并返回一份一致的契约;随后控制权交还给调用方。

概述

此技能是 REST/HTTP API 及其契约的设计规范,位于任何 Web 框架之上。它包含资深工程师在将一组领域名词转化为一致且可演进的 HTTP 界面时采用的明确默认做法:如何命名资源、每种操作采用哪种方法和状态码、统一的机器可读错误格式(RFC 9457 problem+json)、结构化的成功响应和分页封装、版本管理策略、身份验证和速率限制的处理方式,以及如何将这一切呈现为 OpenAPI 3.1 契约。示例代码采用 Python/FastAPI + Pydantic v2 风格,但所有决策都与语言无关。框架机制(路由器、依赖注入、response_model)和 Schema 建模属于 fastapi 和 pydantic-v2 技能的范畴——本技能会引用它们,但不会重复说明。

何时启用

  • ✅ 根据一组领域实体设计新的 API 界面——确定端点、方法和契约。
  • ✅ 为某个结果选择正确的状态码,或决定使用 400 还是 422、401 还是 403、PUT 还是 PATCH。
  • ✅ 定义或修正 API 的错误模型、成功响应封装或分页方案。
  • ✅ 决定 API 的版本管理、身份验证或速率限制策略。
  • ✅ 将达成一致的 REST 设计转化为 OpenAPI 3.1 契约。

不要在以下情况启用:

  • 为已设计的端点编写 FastAPI 处理器/路由器/依赖注入代码 → 使用 fastapi 技能。
  • 深入建模请求/响应数据类 → 使用 pydantic-v2 技能。
  • 编写或生成大型 OpenAPI 文档、检查其规范,或运行 SDK 代码生成 → 这些属于专门的 OpenAPI 编写/代码生成工作(参见 references/openapi-mapping.md,了解设计到契约的映射及交接方式)。
  • 设计 GraphQL 或 gRPC 界面 → 不在适用范围内;本技能仅适用于 REST/HTTP。

工作流

按顺序执行以下步骤。每个步骤都说明了一项明确的默认做法;只有给出理由时才偏离。

步骤 1:建模资源(用名词,而非动词)
  • 资源是名词;HTTP 方法才是动词。使用 POST /orders,绝不使用 POST /createOrder。
  • 集合名称统一使用复数:/users、/users/{id}、/users/{id}/orders。绝不混用 /user 和 /users。
  • 嵌套最多 2 层。 /users/{id}/orders 没问题;/users/{id}/orders/{oid}/items/{iid} 则不行——将深层子资源提升为顶层资源(/order-items/{iid}),并建立关联。
  • 选择 URL 前,先根据领域名词及其关系识别资源。子资源表示包含/所有权关系;横切实体则应成为独立的顶层资源。
  • URL 路径段使用小写字母和连字符(/order-items),不要使用 camelCase 或 snake_case。
  • 不要泄露会暴露行数或可枚举信息的内部/主键 ID——公开契约应优先使用 UUID 或不透明 ID。
第 2 步:分配方法并遵守幂等性

根据 RFC 9110 HTTP 语义:

方法用途安全幂等
GET读取资源/集合是是
POST在集合中创建资源;执行非幂等操作否否
PUT在已知 URL 上完整替换资源否是
PATCH部分更新(仅更新发送的字段)否否
DELETE删除资源否是
  • PUT 用于替换,PATCH 用于合并。 PUT 请求正文是完整的新表示;未提供的字段会被清除。PATCH 请求正文只包含要更改的字段。部分更新不要使用 PUT。
  • POST 和 PATCH 不具备幂等性:重试 POST 会创建重复项。对于客户端可能重试的创建/扣款操作,应接受 Idempotency-Key 请求头(Stripe 采用的约定,现已成为 IETF 草案)——存储密钥和首次响应,并在收到重复请求时重放该响应,这样网络重试就是安全的。
  • GET 不得产生副作用。如果某项操作会修改状态,就不属于 GET。
第 3 步:选择与结果相符的状态码
代码含义/适用情况
200 OKGET/PATCH/PUT 成功,或 DELETE 成功且返回正文
201 Created创建成功——设置 Location 响应头,指向新资源的 URL
204 No Content操作成功且无正文(通常用于 DELETE,或不返回内容的 PUT/PATCH)
400 Bad Request请求格式错误——JSON 无法解析、结构不正确、查询编码错误
401 Unauthorized未通过身份验证——缺少或凭据无效
403 Forbidden已通过身份验证,但无权执行此操作
404 Not Found资源不存在(或对当前调用方隐藏)
409 Conflict请求与当前状态冲突——唯一字段重复、版本冲突
422 Unprocessable Content语法有效,但语义无效——业务/字段验证失败
429 Too Many Requests超出速率限制——包含 Retry-After
5xx服务器故障——500 表示一般错误,503 表示服务不可用;绝不要归咎于客户端
  • 400 与 422: 400 表示服务器根本无法解析/理解请求;422 表示服务器成功解析请求,但其中的值未通过验证。新 API 默认使用 422 表示验证失败,400 表示解析失败。
  • 401 与 403: 401 表示“你是谁”(需要重新进行身份验证);403 表示“我知道你是谁,但你仍无权执行此操作”。
  • 绝不要在错误正文中返回 200。 状态码是首要的机器可读信号——将失败包装在 200 响应中会破坏所有客户端、代理和监控系统。
第 4 步:定义一种错误模型——RFC 9457 problem+json(默认)

每个错误响应都使用 application/problem+json(RFC 9457,取代了 RFC 7807),并包含以下成员:

  • type — 用于标识问题类型的 URI(稳定且有文档说明;如果问题仅由状态码表示,则默认为 "about:blank")。
  • title — 对问题类型的简短说明(对于 about:blank,使用 HTTP 状态短语,例如 "Not Found")。
  • status — HTTP 状态码,在正文中重复提供。
  • detail — 对此次问题的可读说明。
  • instance — 用于标识此次问题的 URI(例如请求路径或跟踪 ID)。
  • 扩展成员 — 其他任何领域字段;客户端 MUST 忽略不认识的成员。字段级验证错误放在 errors 数组中(RFC 认可的扩展)。
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Your request parameters did not validate.",
  "status": 422,
  "detail": "The request body has 2 invalid fields.",
  "instance": "/orders",
  "errors": [
    { "detail": "must be a positive integer", "pointer": "#/quantity" },
    { "detail": "must be a valid email", "pointer": "#/customer/email" }
  ]
}
  • 为整个 API 选择一种模型,绝不要混用。RFC 9457 是默认选择,因为它是一种标准、可机器解析且自描述的格式。
  • 替代方案(仅在项目规范强制要求时使用): 封装格式 {"error": {"code": "...", "message": "...", "details": [...]}}。这种格式可用,但不是标准;新 API 应优先使用 problem+json。不要同时使用两种格式。
  • FastAPI 用法:注册一个返回 application/problem+json 的异常处理器;将 RequestValidationError 映射为包含 errors 数组的 422 问题响应。参见 references/error-model.md。
步骤 5:将成功响应和分页整合为一个信封
  • 单个资源:返回该资源的表示形式。如果对所有地方都采用轻量且一致的包装形式({"data": {...}}),也完全可以——一致性胜过巧思。
  • 集合始终分页——绝不返回无界列表。默认 limit 为 20,硬上限为 100;超过上限则返回 422。
  • 所有列表端点都使用同一种信封结构:data(项目)+ pagination/meta(游标/计数)+ 可选的 links(导航 URL)。
  • 对大型、不断变化或类似信息流的数据,默认使用游标(键集)分页:插入数据时保持稳定,深分页耗时固定,且无需昂贵的总数统计。只有对于需要随机跳转页面或总数的小型稳定数据集,才使用偏移量/页码分页——并发写入时它会发生偏移(漏项/重复),深分页也会变得非常慢。
  • 两种信封结构以及游标与偏移量的选择,请参阅 references/contract-patterns.md。
步骤 6:决定版本控制、身份验证和速率限制
  • 版本控制——默认使用 URL 路径: /v1/users。清晰可见、易于调试、便于缓存,并允许多个版本并行运行。若整洁的 URL 比易于发现更重要,可选择基于请求头/媒体类型的版本控制。
  • 仅在发生破坏性变更时才提升主版本号(删除/重命名字段、更改类型、添加必填字段,或更改同一场景下的状态码)。增量变更(新增端点、新增可选字段)绝不提升主版本号——应将客户端设计为容忍型读取器,忽略未知字段。
  • 使用 Deprecation 和 Sunset 请求头标示弃用(RFC 8594);日落后返回 410 Gone。
  • **身份验证属于契约的一部分,而非事后补充:**面向用户的 API 默认使用 Authorization: Bearer <token>(JWT/OAuth);服务间调用使用 API 密钥请求头(例如 X-API-Key)。**始终使用 HTTPS。**在 OpenAPI 中,这些对应于 securitySchemes 和 security 要求(可按操作配置,也可全局配置)。
  • **速率限制:**返回 429 + Retry-After(稳定且符合 RFC-9110 的默认方案)。使用广泛部署的 X-RateLimit-Limit/-Remaining/-Reset 约定公布配额;IETF 的 RateLimit/RateLimit-Policy 字段是正在形成的标准,但尚未成为 RFC——429 + Retry-After 是所有客户端都能理解的安全基线。
步骤 7:用 OpenAPI 3.1 契约表达设计

交付内容是一份可直接映射设计的 OpenAPI 3.1 文档:资源→paths,方法→操作,信封/错误/实体结构→components/schemas,复用的响应/参数通过 $ref 对应到 components/responses+components/parameters,身份验证→components/securitySchemes。使用 3.1(完整支持 JSON Schema 2020-12:可空值使用 type: ["string","null"],而非旧版的 nullable)。每项设计提供一份完整示例契约,到此为止——深入编写 OpenAPI、校验、模拟服务和 SDK 代码生成属于其他范畴。完整的设计→契约映射及一份完整示例契约见 references/openapi-mapping.md。

规则

硬性规则(绝不违反):

  • 整个 API 采用一种错误模型;默认使用 RFC 9457 problem+json,以 application/problem+json 提供。绝不混用两种错误格式。
  • 绝不在响应正文包含错误时返回 200(或任何 2xx)。状态码就是契约。
  • 资源使用名词;路径中绝不使用动词。集合名称使用复数,并保持一致。
  • 每个集合端点都必须分页,并强制执行最大页大小限制。
  • 始终使用 HTTPS;在契约中声明身份验证(securitySchemes + security),绝不隐式处理。
  • PUT 用于完整替换,PATCH 用于部分更新;绝不混淆两者。
  • 契约使用 OpenAPI 3.1(JSON Schema 2020-12);通过 "null" 类型成员表示可空性,不使用已移除的 3.0 nullable 关键字。

偏好(可调整):

  • 默认采用游标/键集分页;仅对小型、稳定且支持随机访问的数据集使用偏移分页。
  • 默认采用 URL 路径版本控制;仅使用主版本号,向后兼容的新增内容不提升版本。
  • 用户 API 使用 Bearer/JWT,服务间调用使用 API 密钥。
  • 以 429 + Retry-After 作为速率限制基线;添加 X-RateLimit-* 提示性标头。
  • 资源嵌套最多 2 层;将更深层的子资源提升为顶层资源。

易错点

  • 将 PATCH 当作完整替换会悄无声息地清除客户端未发送的字段。PATCH 必须只合并提供的字段;如果需要替换语义,请使用 PUT。
  • 正文包含错误但状态码为 200在浏览器中看似正常,却会破坏基于状态码的重试逻辑、缓存和监控。务必设置真实状态码。
  • **偏移分页漂移:**并发插入/删除时,?offset= 会导致不同页面之间漏掉或重复行;偏移量越大,速度也越慢(数据库会扫描并丢弃所有跳过的行)。游标/键集分页可以避免这两个问题。
  • **混淆 422 和 400:**对于已成功解析但未通过业务验证的值返回 400,会丢失客户端用于分支处理的信息。将 400 留给“完全无法解析”的情况。
  • **OpenAPI 3.1 架构中的 nullable: true**会被悄无声息地忽略——这是 3.0 的关键字。请使用 type: ["string", "null"]。验证器不一定会发出警告。
  • **假设 POST 具有幂等性:**客户端(和代理)可能会重试超时的 POST,从而创建重复项。如果预期会发生重试,请要求提供 Idempotency-Key,并在服务端进行去重。
  • 泄露连续的内部 ID(/users/1001)会暴露行数并使枚举成为可能。请在公开接口中使用不透明 ID/UUID。

反模式

  • “我们就返回 200,然后在正文里放个 ok: false”——不行。状态码是机器可读的契约。
  • “动词更清楚,所以 /getUser 没问题”——不行。方法是动词;路径是名词。
  • “每个子系统使用一种错误格式”——选择一种(problem+json),并在所有地方统一使用。
  • “跳过分页吧,这个集合很小”——集合会不断增长;无界列表是潜在的故障隐患。
  • “为每个端点单独做版本控制/在 URL 中使用次版本号”——对整个 API 进行版本控制,且仅使用主版本号。
  • “在这里重新讲解 FastAPI 路由/Pydantic 建模”——请参阅 fastapi 和 pydantic-v2 技能;本技能负责设计,而不是框架代码。

输出

此技能会生成一份REST API 设计及其 OpenAPI 3.1 契约:资源/URL 映射、每项操作对应的方法和状态码决策、统一的错误模型(problem+json)和成功/分页封套、版本控制/身份验证/速率限制方案,以及一份完整示例 OpenAPI 3.1 文档来表达这些内容。抽象的使用者是负责实现处理程序的工程师(或编码 Agent)——使用 fastapi 和 pydantic-v2 技能——以及依据已发布契约进行集成的下游客户端。

相关内容

  • fastapi — 框架机制(路由器、依赖注入、response_model、异常处理器)。本技能负责确定契约;fastapi 负责实现。
  • pydantic-v2 — 请求/响应模式建模。本技能负责命名数据结构;pydantic-v2 负责对其建模。
  • OpenAPI-authoring / SDK-codegen 工具 — 更深入的契约编写、 lint、模拟服务和客户端生成(属于本技能交由其他工具处理的独立事项)。
  • references/error-model.md、references/contract-patterns.md、references/openapi-mapping.md — 加载条件见下文。

渐进式披露

大量内容位于 references/ 中,仅按需加载:

  • references/error-model.md — 定义或修复错误模型时加载:RFC 9457 逐成员说明、errors 验证数组、about:blank 默认值、404 和 422 的 problem+json 示例、FastAPI 异常处理器模式,以及用于对比的非标准 {error:{...}} 替代方案。
  • references/contract-patterns.md — 设计响应或分页时加载:成功响应封装、包含两种响应结构的游标分页与偏移分页决策表、版本生命周期(Deprecation/Sunset/410)、身份验证方案和速率限制标头。
  • references/openapi-mapping.md — 将设计表述为契约时加载:设计→OpenAPI-3.1 元素映射、3.1 与 3.0 的说明(JSON Schema 2020-12、null 类型、webhook),以及一个包含 problem+json 响应和安全方案的完整契约示例。
  • references/sources.md — 研究来源 / 引用。

正文预算

  • description ≤ 1,024 个字符(agentskills.io 上限)。
  • 正文 ≤ 约 500 行 / 5,000 个 token — 每轮都保留在上下文中。
  • 每个参考文件:超过 10k 个 token 时警告,超过 25k 个 token 时报错。参考文件总量:超过 25k 个 token 时警告,超过 50k 个 token 时报错。

评论

支持 Markdown