- AI 技能
- AI 插件
【免费下载链接】agentic-awesome-skills
AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,400+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.
导读
本指南以 agentic-awesome-skills 仓库中 api-design-principles 技能 为核心骨架展开,系统讲解从需求定义、风格选型、契约建模到错误处理、版本化、分页与鉴权策略的完整 API 设计方法论。读完本文,你将掌握一套可直接落地到团队评审与编码环节的 REST/GraphQL 设计检查清单,并能结合仓库提供的 FastAPI 可运行模板,把设计规范转化为可执行的接口实现。
一、技能定位:何时使用、何时避免
该技能面向"接口契约设计"这一层,而非具体框架的实现细节。它定义了清晰的适用边界:
适用场景(Use this skill when):
- 设计新的 REST 或 GraphQL API;
- 重构既有 API 以提升可用性;
- 为团队建立 API 设计标准;
- 在实现前评审 API 规范;
- 在 API 范式之间迁移(如 REST 迁移到 GraphQL);
- 编写对开发者友好的 API 文档;
- 针对特定使用场景(移动端、第三方集成)优化 API。
不适用场景(Do not use this skill when):
- 只需要某个特定框架的实现指导;
- 只做基础设施工作、不涉及 API 契约;
- 无法修改或对公共接口做版本化。
这一"用/不用"二分法非常关键:API 设计决策一旦进入公共契约阶段,改动成本极高,因此技能在源头上就提示使用者,只有当你拥有接口变更权与版本演进空间时,才应启动这套流程。设计产出的边界在 SKILL.md 中有明确约束:它不能替代环境特定的验证、测试与专家评审,当关键输入(需求、权限、安全边界、成功标准)缺失时应停下询问,而不是凭猜测继续。
二、四步设计工作流:从消费者定义到一致性评审
技能把 API 设计收敛为一个可重复的四步流程,每一步都有明确的产出物:
定义消费者、使用场景与约束(Define consumers, use cases, and constraints)。先回答"谁在用、怎么用、限制是什么":是移动端弱网场景还是服务端高频调用?是第三方公开集成还是内部微服务?约束包括吞吐、延迟、数据规模、合规要求等。这一步骤决定了后续所有取舍的依据。
选择 API 风格并建模资源或类型(Choose API style and model resources or types)。在 REST 与 GraphQL 之间做范式选择,然后建模:REST 用名词化资源 + HTTP 方法表达操作,GraphQL 用类型系统(Type/Interface/Union/Input)表达领域模型。
明确错误、版本化、分页与鉴权策略(Specify errors, versioning, pagination, and auth strategy)。这是公共契约中"变不了"的部分,必须在实现前定稿:统一的错误响应结构、可执行的版本演进策略、稳定的分页约定、清晰的鉴权/授权边界。
用示例验证并做一致性评审(Validate with examples and review for consistency)。用真实请求/响应示例跑通每个端点,并对照 api-design-checklist.md 逐项检查命名、状态码、分页、错误、安全等维度的一致性。
仓库为这套流程准备了三个可复用的落地资产,对应工作流的不同阶段:references/rest-best-practices.md(REST 细则)、references/graphql-schema-design.md(GraphQL 模式库)与assets/api-design-checklist.md(评审清单)、assets/rest-api-template.py(可运行模板),下文逐一展开。
三、REST API 设计核心规范
rest-best-practices.md 提供了完整的 REST 设计细则,覆盖从 URL 到监控的全生命周期。
3.1 URL 结构与资源命名
资源必须是复数名词,禁止用动词表达操作,命名在整个 API 中保持一致:
# Good - Plural nouns GET /api/users GET /api/orders GET /api/products # Bad - Verbs or mixed conventions GET /api/getUser GET /api/user (inconsistent singular) POST /api/createOrder嵌套资源遵循"浅嵌套优先"原则:GET /api/users/{id}/orders这样的两级关系可以接受,但超过两级的深层嵌套应当被拍平——把深层项提升为顶层资源,通过查询参数关联:
# Deep nesting (avoid) GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews # Better: GET /api/order-items/{id}/reviews3.2 HTTP 方法与状态码映射
每个方法承担单一语义,且必须保持幂等性预期:
| 方法 | 语义 | 幂等 | 典型响应 |
|---|---|---|---|
| GET | 检索 | 是(安全) | 200 OK / 404 Not Found |
| POST | 创建 | 否 | 201 Created(带Location头) |
| PUT | 整体替换 | 是 | 200 OK / 404 |
| PATCH | 部分更新 | 否 | 200 OK |
| DELETE | 删除 | 是 | 204 No Content / 404 / 409 Conflict |
创建成功时必须返回201 Created并携带Location: /api/users/123;校验失败返回422 Unprocessable Entity;DELETE 因存在外键引用无法删除时返回409 Conflict。
3.3 过滤、排序与搜索
统一使用查询参数表达三种能力,命名稳定可预测:
# Filtering GET /api/users?status=active GET /api/users?role=admin&status=active # Sorting(负号表示降序,逗号表示多字段) GET /api/users?sort=created_at GET /api/users?sort=-created_at GET /api/users?sort=name,created_at # Searching GET /api/users?search=john # Field selection(稀疏字段集) GET /api/users?fields=id,name,email3.4 三种分页模式与选型
Offset 分页适合中小数据集,响应中必须包含分页元数据(total/page/pages),默认页大小与上限要显式定义(例如默认 20、上限 100):
GET /api/users?page=2&page_size=20 # Response: { "items": [...], "page": 2, "page_size": 20, "total": 150, "pages": 8 }Cursor 分页适合大数据集与无限滚动,以不透明游标代替页码,避免插入/删除导致的结果漂移:
GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ # Response: { "items": [...], "next_cursor": "eyJpZCI6MTQzfQ", "has_more": true }Link Header 分页是纯 RESTful 风格,把导航信息放进响应头而不是业务字段:
Link: <https://api.example.com/users?page=3>; rel="next", <https://api.example.com/users?page=1>; rel="prev", <https://api.example.com/users?page=1>; rel="first", <https://api.example.com/users?page=8>; rel="last"3.5 版本化策略对比
参考文档给出了三种策略及权衡:
| 策略 | 示例 | 优点 | 缺点 |
|---|---|---|---|
| URL 版本化(推荐) | /api/v1/users | 清晰、易路由 | 同一资源多个 URL |
| Header 版本化 | Accept: application/vnd.api+json; version=2 | URL 干净 | 不直观、难测试 |
| Query 参数 | ?version=2 | 易测试 | 可选参数易被遗忘 |
3.6 限流:头部约定与实现模式
限流信息应通过标准响应头暴露,超限时返回429 Too Many Requests并附带Retry-After:
X-RateLimit-Limit: 1000 X-RateLimit-Remaining: 742 X-RateLimit-Reset: 1640000000 # Limited: 429 Too Many Requests Retry-After: 3600参考文档给出了一个基于内存时间窗的可运行实现,核心是滑动窗口去旧加新:
from fastapi import HTTPException, Request from datetime import datetime, timedelta class RateLimiter: def __init__(self, calls: int, period: int): self.calls = calls self.period = period self.cache = {} def check(self, key: str) -> bool: now = datetime.now() if key not in self.cache: self.cache[key] = [] # Remove old requests self.cache[key] = [ ts for ts in self.cache[key] if now - ts < timedelta(seconds=self.period) ] if len(self.cache[key]) >= self.calls: return False self.cache[key].append(now) return True limiter = RateLimiter(calls=100, period=60) @app.get("/api/users") async def get_users(request: Request): if not limiter.check(request.client.host): raise HTTPException( status_code=429, headers={"Retry-After": "60"} ) return {"users": [...]}3.7 认证与授权:401 vs 403
认证(你是谁)与授权(你能干什么)必须严格区分:401 Unauthorized表示缺失或无效的 Token(未认证),403 Forbidden表示 Token 有效但权限不足(已认证未授权)。
Authorization: Bearer eyJhbGciOiJIUzI1NiIs... # Bearer Token X-API-Key: your-api-key-here # API Key3.8 统一错误响应结构
错误响应必须稳定、结构化、可被客户端程序化处理,包含错误码、人类可读消息、字段级详情、时间戳与路径:
{ "error": { "code": "VALIDATION_ERROR", "message": "Request validation failed", "details": [ { "field": "email", "message": "Invalid email format", "value": "not-an-email" } ], "timestamp": "2025-10-16T12:00:00Z", "path": "/api/users" } }完整状态码速查表:200 表示 GET/PATCH/PUT 成功;201 表示 POST 成功;204 表示 DELETE 成功;400 请求格式错误;401 需要认证;403 已认证但无权限;404 资源不存在;409 状态冲突(如重复邮箱);422 校验失败;429 被限流;500 服务端错误;503 临时不可用。
3.9 缓存、批量、幂等、CORS
缓存:通过Cache-Control控制客户端缓存;用ETag+If-None-Match实现条件请求,命中时返回304 Not Modified:
Cache-Control: public, max-age=3600 ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4" If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4" → 304 Not Modified批量端点:批量操作返回逐条结果状态,允许部分成功:
POST /api/users/batch {"items": [{"name": "User1", "email": "user1@example.com"}, ...]} # Response: {"results": [{"id": "1", "status": "created"}, {"id": null, "status": "failed", "error": "Email already exists"}]}幂等键:POST 类非幂等操作通过Idempotency-Key头实现重试安全,重复请求返回首次缓存响应而非重复执行。
CORS:生产环境必须显式限定allow_origins,只有确需携带 Cookie/鉴权头时才开allow_credentials=True(此时禁止通配符来源)。
3.10 OpenAPI 文档与健康检查
FastAPI 天然把设计即文档落在实处:FastAPI(title=..., version=..., docs_url="/api/docs", redoc_url="/redoc")自动生成交互式文档;每个端点用summary、response_description、tags与 docstring 丰富契约语义。健康检查应区分浅层与深层:
@app.get("/health") async def health_check(): return {"status": "healthy", "version": "1.0.0", "timestamp": datetime.now().isoformat()} @app.get("/health/detailed") async def detailed_health(): return {"status": "healthy", "checks": {"database": await check_database(), "redis": await check_redis(), "external_api": await check_external_api()}}四、GraphQL Schema 设计模式
graphql-schema-design.md 给出了从 Schema 组织到性能防护的完整模式库。GraphQL 的核心挑战是把"灵活查询"控制在安全边界内,因此本节的每条模式都同时回答"怎么设计"与"怎么防滥用"。
4.1 Schema 组织与模块化
按领域拆分 Schema 文件,用extend type归并 Query/Mutation 根类型,避免巨型单文件:
# user.graphql type User { id: ID!, email: String!, name: String!, posts: [Post!]! } extend type Query { user(id: ID!): User users(first: Int, after: String): UserConnection! } extend type Mutation { createUser(input: CreateUserInput!): CreateUserPayload! }4.2 类型设计四件套
非空 vs 可空:id: ID!、email: String!表示必有字段;phone: String表示可空;posts: [Post!]!是非空数组内含非空元素,tags: [String!]是可空数组内含非空字符串。经验法则是:从可空开始,当业务保证存在时再升级为非空,避免 schema 变更炸掉客户端。
Interface 表达多态:公共字段(id/createdAt)抽到接口,各类型implements Node,Query 返回接口类型:
interface Node { id: ID!, createdAt: DateTime! } type User implements Node { id: ID!, createdAt: DateTime!, email: String! } type Post implements Node { id: ID!, createdAt: DateTime!, title: String! } type Query { node(id: ID!): Node }Union 表达异构结果:搜索这类返回"不同类型混合"的场景用 union,配合内联片段消费:
union SearchResult = User | Post | Comment { search(query: "graphql") { ... on User { name email } ... on Post { title content } ... on Comment { text author { name } } } }Input Type:所有 mutation 参数必须封装为 input 类型,支持嵌套 input(如profileInput: ProfileInput),更新场景用全可选 input 表达部分更新。
4.3 分页:Relay Cursor 连接模型
大数据集推荐 Relay Cursor 连接模型,edges/node/cursor加pageInfo,客户端用first/after与last/before双向翻页:
type UserConnection { edges: [UserEdge!]! pageInfo: PageInfo! totalCount: Int! } type UserEdge { node: User!, cursor: String! } type PageInfo { hasNextPage: Boolean!, hasPreviousPage: Boolean! startCursor: String, endCursor: String } type Query { users(first: Int, after: String, last: Int, before: String): UserConnection! }简单场景可用 offset 分页(page/pageSize/items/total),但无限滚动场景应首选游标。
4.4 Mutation 设计三模式
Input/Payload 模式:mutation 接收input,返回payload,payload 内嵌errors与success,错误不依赖 GraphQL 传输层异常:
input CreatePostInput { title: String!, content: String!, tags: [String!] } type CreatePostPayload { post: Post errors: [Error!] success: Boolean! } type Mutation { createPost(input: CreatePostInput!): CreatePostPayload! }乐观响应支持:payload 携带clientMutationId,让客户端把服务端回执映射回本地乐观 UI 状态。
批量 Mutation:返回逐条结果、成功数与失败数:
type BatchCreateUserPayload { results: [CreateUserResult!]! successCount: Int!, errorCount: Int! } type CreateUserResult { user: User, errors: [Error!], index: Int! }4.5 字段参数、计算字段与订阅
字段参数按"分页/过滤/排序/搜索"分组声明,用 enum 限定排序键与方向;计算字段(postCount、isLikedByViewer)在 resolver 中按需计算,避免加载全量关联数据。实时场景用 Subscription,可携带参数做定向推送(如postUpdated(postId: ID!))。
4.6 自定义标量与指令
领域类型用自定义标量表达,避免 String 滥用:scalar DateTime / Email / URL / JSON / Money。内置指令@deprecated标记退役字段、@include(if:)做条件字段;自定义指令可用于声明式鉴权:
directive @auth(requires: Role = USER) on FIELD_DEFINITION enum Role { USER ADMIN MODERATOR } type Mutation { deleteUser(id: ID!): Boolean! @auth(requires: ADMIN) updateProfile(input: ProfileInput!): User! @auth }4.7 GraphQL 错误处理:Union 错误模式与 Payload 错误
查询场景用Union 错误模式——把错误建模为一等类型并入返回联合,客户端用内联片段区分成功与各错误分支:
union UserResult = User | ValidationError | NotFoundError | AuthorizationError type Query { user(id: ID!): UserResult! }变更场景用Payload 错误模式(见 4.4),错误码用 enum 约束(VALIDATION_ERROR / UNAUTHORIZED / NOT_FOUND / INTERNAL_ERROR)。
4.8 N+1 与查询防护
DataLoader是 N+1 的标准解:按关系批量加载并做内存缓存,resolver 中从 context 取 loader 批量读取,避免逐条查库:
class PostLoader(DataLoader): async def batch_load_fn(self, post_ids): posts = await db.posts.find({"id": {"$in": post_ids}}) post_map = {post["id"]: post for post in posts} return [post_map.get(pid) for pid in post_ids]查询深度限制(防递归炸弹)与查询复杂度分析(列表字段按大小参数加权)双管齐下:
def depth_limit_validator(max_depth: int): def validate(context, node, ancestors): depth = len(ancestors) if depth > max_depth: raise GraphQLError(f"Query depth {depth} exceeds maximum {max_depth}") return validate def complexity_limit_validator(max_complexity: int): def calculate_complexity(node): complexity = 1 if is_list_field(node): complexity *= get_list_size_arg(node) return complexity return validate_complexity4.9 Schema 版本演进
GraphQL 的演进哲学是"永远别删,只废弃":新增可选字段向后兼容(v1→v2);替换字段时先@deprecated(reason: ...)再逐步移除(v3)。配合@deprecated与字段描述文档化,客户端有充足迁移窗口。
五、落地工具一:API 设计评审清单
api-design-checklist.md 把上述全部规范折叠成一份可勾选的评审清单,分为实现前评审与GraphQL 专属检查两大块。
实现前评审覆盖 11 个维度:资源设计(名词化、复数、一致性、层级 ≤2 级、CRUD 映射完整)、HTTP 方法语义、状态码(10 个关键码)、分页(全端点覆盖、默认 20 上限 100、元数据、模式选定)、过滤/排序/搜索/稀疏字段集、版本化策略、错误处理(统一格式、字段级校验、错误码、时间戳)、认证授权、限流(头部、429、Retry-After)、文档(OpenAPI、示例、错误文档)、测试(单测/集成/错误场景/边界/性能)、安全(输入校验、SQL 注入、XSS、CORS、HTTPS、敏感数据不进 URL、响应无密钥)、性能(查询优化、防 N+1、缓存策略、缓存头、分页大响应)、监控(日志、错误追踪、指标、健康检查、告警)。
GraphQL 专属检查聚焦:Schema 优先、类型定义、非空决策、接口/联合、自定义标量;查询深度限制、复杂度分析、DataLoader 防 N+1、分页模式;mutation 的 input/payload/乐观响应/幂等;性能的批处理、持久化查询、响应缓存;以及字段文档、废弃标记、自省开关。这份清单可直接作为团队 PR 评审的硬门槛。
六、落地工具二:FastAPI 可运行模板剖析
rest-api-template.py 是上述规范的可执行化示范,采用 FastAPI + Pydantic v2,可作为新项目骨架。它把规范逐条映射为代码,值得逐段对照理解:
- 中间件层:
TrustedHostMiddleware防 HTTP Host 头攻击,CORSMiddleware管控跨域——两处均以 TODO 标注生产环境必须收紧(allowed_hosts=["*"]、allow_origins=["*"]仅为开发便利); - 模型层:用
Enum限定UserStatus;UserBase/UserCreate/UserUpdate分层建模——创建态含password(min_length=8强校验),更新态全字段可选并带范围约束(min_length=1, max_length=100);User输出模型含id/created_at/updated_at并开启from_attributes=True支持 ORM 对象序列化; - 分页契约:
PaginationParams用Field(1, ge=1)与Field(20, ge=1, le=100)把"默认 20、上限 100"直接编码进 Pydantic 约束;PaginatedResponse固定返回items/total/page/page_size/pages五元组,与参考文档的 offset 分页响应完全一致; - 统一错误处理:
ErrorDetail/ErrorResponse实现"code + message + field 级 details"结构;全局@app.exception_handler(HTTPException)把任意异常归一化为统一 JSON 响应,服务端自动兜底错误格式一致性; - 端点语义:
POST显式status_code=201;DELETE返回204;PATCH用model_dump(exclude_unset=True)只更新传入字段,天然实现部分更新语义;404 通过HTTPException抛出并携带结构化 detail。
模板与参考文档的映射关系非常清晰:/api/users?page=&page_size=对应 3.4 节 offset 分页,ErrorResponse对应 3.8 节统一错误结构,中间件对应 3.9 节 CORS 与安全规范。开发时只需将 mock 数据源替换为真实存储即可上线。
七、局限性与正确使用姿势
技能自身的 Limitations 声明(见 SKILL.md)同样适用于本文内容:
- 仅在任务明确匹配上述范围时使用本套方法,不要泛化到框架实现或纯基础设施任务;
- 设计产出不能替代环境特定验证、测试与专家评审——契约纸面合理不等于实现正确;
- 当所需输入(需求、权限、安全边界、成功标准)缺失时,应停下来澄清,而不是在模糊前提下强行定稿。
实际运用建议:新项目按"四步工作流"走完整流程,以清单驱动评审;存量 API 重构则先用错误结构、分页元数据、命名一致性等"低成本高收益"项切入,再逐步引入版本化与限流。仓库中该技能同时存在于 plugins/agentic-awesome-skills/skills/api-design-principles 与 plugins/agentic-awesome-skills-claude/skills/api-design-principles 两个插件目录,两者内容一致,可对照使用;后续在 data/aas-v1/skill-content-index.v1.json 中可检索到该技能在目录索引中的注册信息,便于理解它在整个技能仓库中的组织方式。
- AI 技能
- AI 插件
【免费下载链接】agentic-awesome-skills
AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and planning, backed by 2,400+ agentic skills. Includes CLI, local MCP, catalog, plugins, and Workbench.
相关推荐
agentic-awesome-skills 之 API 设计原则实战手册:REST 与 GraphQL 的工程化落地指南
agentic awesome skills 之 API 设计原则实战手册:REST 与 GraphQL 的工程化落地指南 本指南以 implementatio
AI 技能AI 插件GraphQL 设计原则实战指南:从选型判断到 Schema 与安全防护(agentic-awesome-skills api-patterns 系列)
GraphQL 设计原则实战指南:从选型判断到 Schema 与安全防护(agentic awesome skills api patterns 系列) 本篇以
AI 技能AI 插件基于 agentic-awesome-skills 的 API 设计检查清单:REST 与 GraphQL 上线前逐项审查指南
基于 agentic awesome skills 的 API 设计检查清单:REST 与 GraphQL 上线前逐项审查指南 导读 本文围绕仓库中 api d
AI 技能AI 插件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考