api-design-patterns by asyrafhussin/agent-skills
npx skills add https://github.com/asyrafhussin/agent-skills --skill api-design-patternsRESTful API 设计原则、错误处理、分页、版本控制和安全性最佳实践。构建一致、对开发者友好的 API 的指南。
在以下情况下参考这些指南:
| 优先级 | 类别 | 影响 | 前缀 |
|---|---|---|---|
| 1 | 资源设计 | 关键 | rest- |
| 2 | 错误处理 | 关键 | error- |
| 3 | 安全性 | 关键 | sec- |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 4 | 分页与筛选 | 高 | page- |
| 5 | 版本控制 | 高 | ver- |
| 6 | 响应格式 | 中 | resp- |
| 7 | 文档 | 中 | doc- |
rest-nouns-not-verbs - 端点使用名词rest-plural-resources - 使用复数资源名称rest-http-methods - 正确使用 HTTP 方法rest-nested-resources - 正确的资源嵌套rest-status-codes - 恰当的状态码rest-idempotency - 幂等操作rest-hateoas - 超媒体链接error-consistent-format - 一致的错误结构error-meaningful-messages - 有用的错误信息error-validation-details - 字段级验证错误error-codes - 机器可读的错误代码error-stack-traces - 切勿在生产环境中暴露sec-authentication - 正确的身份验证实现sec-authorization - 资源级权限sec-rate-limiting - 防止滥用sec-input-validation - 验证所有输入sec-cors - CORS 配置sec-sensitive-data - 保护敏感数据page-cursor-based - 大数据集使用游标分页page-offset-based - 简单情况使用偏移分页page-consistent-params - 一致的参数名称page-metadata - 包含分页元数据filter-query-params - 通过查询参数筛选sort-flexible - 灵活的排序选项ver-url-path - URL 路径中包含版本ver-header-based - 头部中包含版本ver-backward-compatible - 保持向后兼容性ver-deprecation - 弃用策略resp-consistent-structure - 一致的响应信封resp-json-conventions - JSON 命名约定resp-partial-responses - 字段选择resp-compression - 响应压缩doc-openapi - OpenAPI/Swagger 规范doc-examples - 请求/响应示例doc-changelog - API 变更日志# ❌ URL 中使用动词 - RPC 风格
GET /getUsers
POST /createUser
PUT /updateUser/123
DELETE /deleteUser/123
# ✅ 名词配合 HTTP 方法 - REST 风格
GET /users # 列出用户
POST /users # 创建用户
GET /users/123 # 获取用户
PUT /users/123 # 更新用户(完整)
PATCH /users/123 # 更新用户(部分)
DELETE /users/123 # 删除用户
# ✅ 逻辑嵌套(最多 2 层)
GET /users/123/orders # 用户的订单
GET /users/123/orders/456 # 特定订单
POST /users/123/orders # 为用户创建订单
# ❌ 嵌套过深
GET /users/123/orders/456/items/789/reviews
# ✅ 适当时进行扁平化
GET /order-items/789/reviews # 直接访问
# GET - 检索资源
GET /users → 200 OK + 数组
GET /users/123 → 200 OK + 对象
GET /users/999 → 404 Not Found
# POST - 创建资源
POST /users → 201 Created + 对象 + Location 头部
POST /users (无效) → 400 Bad Request + 错误信息
# PUT - 完整更新(替换)
PUT /users/123 → 200 OK + 更新后的对象
PUT /users/999 → 404 Not Found
# PATCH - 部分更新
PATCH /users/123 → 200 OK + 更新后的对象
# DELETE - 移除资源
DELETE /users/123 → 204 No Content
DELETE /users/999 → 404 Not Found
# 其他常见状态码
401 Unauthorized # 未认证
403 Forbidden # 已认证但未授权
409 Conflict # 资源状态冲突
422 Unprocessable # 验证失败
429 Too Many Requests # 超出速率限制
500 Internal Error # 服务器错误
503 Service Unavailable # 维护/过载
// ❌ 不一致的错误格式
{ "error": "Not found" }
{ "message": "Invalid email" }
{ "errors": ["Error 1", "Error 2"] }
// ✅ 一致的错误信封
{
"error": {
"code": "VALIDATION_ERROR",
"message": "请求包含无效数据",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "请提供有效的电子邮件地址"
},
{
"field": "password",
"code": "TOO_SHORT",
"message": "密码长度必须至少为 8 个字符"
}
],
"request_id": "req_abc123"
}
}
// ✅ 未找到错误
{
"error": {
"code": "NOT_FOUND",
"message": "未找到 ID 为 123 的用户",
"request_id": "req_def456"
}
}
// ✅ 服务器错误(无敏感细节)
{
"error": {
"code": "INTERNAL_ERROR",
"message": "发生意外错误。请重试。",
"request_id": "req_ghi789"
}
}
// ✅ 基于偏移的分页
GET /users?page=2&per_page=20
{
"data": [...],
"meta": {
"current_page": 2,
"per_page": 20,
"total_pages": 10,
"total_count": 195
},
"links": {
"first": "/users?page=1&per_page=20",
"prev": "/users?page=1&per_page=20",
"next": "/users?page=3&per_page=20",
"last": "/users?page=10&per_page=20"
}
}
// ✅ 基于游标的分页(适用于大数据集)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"has_more": true,
"next_cursor": "eyJpZCI6MTQzfQ"
},
"links": {
"next": "/users?cursor=eyJpZCI6MTQzfQ&limit=20"
}
}
# ✅ 查询参数筛选
GET /users?status=active
GET /users?role=admin&status=active
GET /users?created_after=2024-01-01
# ✅ 排序
GET /users?sort=created_at # 升序(默认)
GET /users?sort=-created_at # 降序(前缀加 -)
GET /users?sort=last_name,-created_at # 多个字段
# ✅ 字段选择(稀疏字段集)
GET /users?fields=id,name,email
GET /users/123?fields=id,name,orders
# ✅ 搜索
GET /users?q=john
GET /users?search=john@example
// ✅ 单一资源
GET /users/123
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"relationships": {
"orders": {
"links": {
"related": "/users/123/orders"
}
}
}
}
}
// ✅ 集合
GET /users
{
"data": [
{ "id": "123", "type": "user", ... },
{ "id": "124", "type": "user", ... }
],
"meta": {
"total_count": 100
},
"links": {
"self": "/users?page=1",
"next": "/users?page=2"
}
}
// ✅ 更简单的信封(也可接受)
{
"user": {
"id": "123",
"name": "John Doe"
}
}
{
"users": [...],
"pagination": {...}
}
# ✅ URL 路径版本控制(推荐 - 显式)
GET /api/v1/users
GET /api/v2/users
# ✅ 头部版本控制
GET /api/users
Accept: application/vnd.myapi.v2+json
# ✅ 查询参数(简单,但不够清晰)
GET /api/users?version=2
# ✅ 包含速率限制头部
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1640995200
# ✅ 速率限制响应
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "RATE_LIMITED",
"message": "请求过多。请在 60 秒后重试。",
"retry_after": 60
}
}
# 对于非 CRUD 操作,使用子资源或操作
# ✅ 子资源风格
POST /users/123/activate
POST /orders/456/cancel
POST /posts/789/publish
# ✅ 或对于复杂操作使用控制器风格
POST /auth/login
POST /auth/logout
POST /auth/refresh
POST /password/reset
POST /password/reset/confirm
// ✅ 一次性验证并返回所有错误
POST /users
{
"email": "invalid",
"password": "short"
}
// 响应:422 Unprocessable Entity
{
"error": {
"code": "VALIDATION_ERROR",
"message": "验证失败",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "必须是有效的电子邮件地址"
},
{
"field": "password",
"code": "TOO_SHORT",
"message": "长度必须至少为 8 个字符",
"meta": {
"min_length": 8,
"actual_length": 5
}
}
]
}
}
// ✅ 包含链接以实现可发现性
{
"data": {
"id": "123",
"status": "pending",
"total": 99.99
},
"links": {
"self": "/orders/123",
"cancel": "/orders/123/cancel",
"pay": "/orders/123/pay",
"items": "/orders/123/items"
},
"actions": {
"cancel": {
"method": "POST",
"href": "/orders/123/cancel"
},
"pay": {
"method": "POST",
"href": "/orders/123/pay",
"fields": [
{ "name": "payment_method", "type": "string", "required": true }
]
}
}
}
// ✅ 批量创建
POST /users/bulk
{
"users": [
{ "name": "User 1", "email": "user1@example.com" },
{ "name": "User 2", "email": "user2@example.com" }
]
}
// 包含部分成功的响应
{
"data": {
"succeeded": [
{ "id": "123", "name": "User 1" }
],
"failed": [
{
"index": 1,
"error": {
"code": "DUPLICATE_EMAIL",
"message": "电子邮件已存在"
}
}
]
},
"meta": {
"total": 2,
"succeeded": 1,
"failed": 1
}
}
审计 API 时,按此格式输出发现的问题:
endpoint - [category] 问题描述
示例:
GET /getUsers - [rest] 应使用名词 '/users' 而非动词 '/getUsers'
POST /users - [error] 400 响应中缺少验证错误详情
GET /users - [page] 列表响应中缺少分页元数据
阅读单独的规则文件以获取详细说明:
rules/rest-http-methods.md
rules/error-consistent-format.md
rules/page-cursor-based.md
每周安装量
64
代码仓库
GitHub 星标
11
首次出现
2026年1月21日
安全审计
安装于
codex51
gemini-cli50
opencode49
claude-code46
github-copilot43
cursor41
RESTful API design principles, error handling, pagination, versioning, and security best practices. Guidelines for building consistent, developer-friendly APIs.
Reference these guidelines when:
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Resource Design | CRITICAL | rest- |
| 2 | Error Handling | CRITICAL | error- |
| 3 | Security | CRITICAL | sec- |
| 4 | Pagination & Filtering | HIGH | page- |
| 5 | Versioning | HIGH | ver- |
| 6 | Response Format | MEDIUM | resp- |
| 7 | Documentation | MEDIUM | doc- |
rest-nouns-not-verbs - Use nouns for endpointsrest-plural-resources - Use plural resource namesrest-http-methods - Correct HTTP method usagerest-nested-resources - Proper resource nestingrest-status-codes - Appropriate status codesrest-idempotency - Idempotent operationsrest-hateoas - Hypermedia linkserror-consistent-format - Consistent error structureerror-meaningful-messages - Helpful error messageserror-validation-details - Field-level validation errorserror-codes - Machine-readable error codeserror-stack-traces - Never expose in productionsec-authentication - Proper auth implementationsec-authorization - Resource-level permissionssec-rate-limiting - Prevent abusesec-input-validation - Validate all inputsec-cors - CORS configurationsec-sensitive-data - Protect sensitive datapage-cursor-based - Cursor pagination for large datasetspage-offset-based - Offset pagination for simple casespage-consistent-params - Consistent parameter namespage-metadata - Include pagination metadatafilter-query-params - Filter via query parameterssort-flexible - Flexible sorting optionsver-url-path - Version in URL pathver-header-based - Version in headersver-backward-compatible - Maintain compatibilityver-deprecation - Deprecation strategyresp-consistent-structure - Consistent response enveloperesp-json-conventions - JSON naming conventionsresp-partial-responses - Field selectionresp-compression - Response compressiondoc-openapi - OpenAPI/Swagger specdoc-examples - Request/response examplesdoc-changelog - API changelog# ❌ Verbs in URLs - RPC style
GET /getUsers
POST /createUser
PUT /updateUser/123
DELETE /deleteUser/123
# ✅ Nouns with HTTP methods - REST style
GET /users # List users
POST /users # Create user
GET /users/123 # Get user
PUT /users/123 # Update user (full)
PATCH /users/123 # Update user (partial)
DELETE /users/123 # Delete user
# ✅ Logical nesting (max 2 levels)
GET /users/123/orders # User's orders
GET /users/123/orders/456 # Specific order
POST /users/123/orders # Create order for user
# ❌ Too deeply nested
GET /users/123/orders/456/items/789/reviews
# ✅ Flatten when appropriate
GET /order-items/789/reviews # Direct access
# GET - Retrieve resource(s)
GET /users → 200 OK + array
GET /users/123 → 200 OK + object
GET /users/999 → 404 Not Found
# POST - Create resource
POST /users → 201 Created + object + Location header
POST /users (invalid) → 400 Bad Request + errors
# PUT - Full update (replace)
PUT /users/123 → 200 OK + updated object
PUT /users/999 → 404 Not Found
# PATCH - Partial update
PATCH /users/123 → 200 OK + updated object
# DELETE - Remove resource
DELETE /users/123 → 204 No Content
DELETE /users/999 → 404 Not Found
# Other common status codes
401 Unauthorized # Not authenticated
403 Forbidden # Authenticated but not authorized
409 Conflict # Resource state conflict
422 Unprocessable # Validation failed
429 Too Many Requests # Rate limited
500 Internal Error # Server error
503 Service Unavailable # Maintenance/overload
// ❌ Inconsistent error formats
{ "error": "Not found" }
{ "message": "Invalid email" }
{ "errors": ["Error 1", "Error 2"] }
// ✅ Consistent error envelope
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request contains invalid data",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Please provide a valid email address"
},
{
"field": "password",
"code": "TOO_SHORT",
"message": "Password must be at least 8 characters"
}
],
"request_id": "req_abc123"
}
}
// ✅ Not found error
{
"error": {
"code": "NOT_FOUND",
"message": "User with ID 123 not found",
"request_id": "req_def456"
}
}
// ✅ Server error (no sensitive details)
{
"error": {
"code": "INTERNAL_ERROR",
"message": "An unexpected error occurred. Please try again.",
"request_id": "req_ghi789"
}
}
// ✅ Offset-based pagination
GET /users?page=2&per_page=20
{
"data": [...],
"meta": {
"current_page": 2,
"per_page": 20,
"total_pages": 10,
"total_count": 195
},
"links": {
"first": "/users?page=1&per_page=20",
"prev": "/users?page=1&per_page=20",
"next": "/users?page=3&per_page=20",
"last": "/users?page=10&per_page=20"
}
}
// ✅ Cursor-based pagination (for large datasets)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"has_more": true,
"next_cursor": "eyJpZCI6MTQzfQ"
},
"links": {
"next": "/users?cursor=eyJpZCI6MTQzfQ&limit=20"
}
}
# ✅ Query parameter filtering
GET /users?status=active
GET /users?role=admin&status=active
GET /users?created_after=2024-01-01
# ✅ Sorting
GET /users?sort=created_at # Ascending (default)
GET /users?sort=-created_at # Descending (prefix with -)
GET /users?sort=last_name,-created_at # Multiple fields
# ✅ Field selection (sparse fieldsets)
GET /users?fields=id,name,email
GET /users/123?fields=id,name,orders
# ✅ Search
GET /users?q=john
GET /users?search=john@example
// ✅ Single resource
GET /users/123
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-01-15T10:30:00Z"
},
"relationships": {
"orders": {
"links": {
"related": "/users/123/orders"
}
}
}
}
}
// ✅ Collection
GET /users
{
"data": [
{ "id": "123", "type": "user", ... },
{ "id": "124", "type": "user", ... }
],
"meta": {
"total_count": 100
},
"links": {
"self": "/users?page=1",
"next": "/users?page=2"
}
}
// ✅ Simpler envelope (also acceptable)
{
"user": {
"id": "123",
"name": "John Doe"
}
}
{
"users": [...],
"pagination": {...}
}
# ✅ URL path versioning (recommended - explicit)
GET /api/v1/users
GET /api/v2/users
# ✅ Header versioning
GET /api/users
Accept: application/vnd.myapi.v2+json
# ✅ Query parameter (simple, but less clean)
GET /api/users?version=2
# ✅ Include rate limit headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 998
X-RateLimit-Reset: 1640995200
# ✅ Rate limited response
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please retry after 60 seconds.",
"retry_after": 60
}
}
# For non-CRUD actions, use sub-resources or actions
# ✅ Sub-resource style
POST /users/123/activate
POST /orders/456/cancel
POST /posts/789/publish
# ✅ Or controller-style for complex operations
POST /auth/login
POST /auth/logout
POST /auth/refresh
POST /password/reset
POST /password/reset/confirm
// ✅ Validate and return all errors at once
POST /users
{
"email": "invalid",
"password": "short"
}
// Response: 422 Unprocessable Entity
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Must be a valid email address"
},
{
"field": "password",
"code": "TOO_SHORT",
"message": "Must be at least 8 characters",
"meta": {
"min_length": 8,
"actual_length": 5
}
}
]
}
}
// ✅ Include links for discoverability
{
"data": {
"id": "123",
"status": "pending",
"total": 99.99
},
"links": {
"self": "/orders/123",
"cancel": "/orders/123/cancel",
"pay": "/orders/123/pay",
"items": "/orders/123/items"
},
"actions": {
"cancel": {
"method": "POST",
"href": "/orders/123/cancel"
},
"pay": {
"method": "POST",
"href": "/orders/123/pay",
"fields": [
{ "name": "payment_method", "type": "string", "required": true }
]
}
}
}
// ✅ Bulk create
POST /users/bulk
{
"users": [
{ "name": "User 1", "email": "user1@example.com" },
{ "name": "User 2", "email": "user2@example.com" }
]
}
// Response with partial success
{
"data": {
"succeeded": [
{ "id": "123", "name": "User 1" }
],
"failed": [
{
"index": 1,
"error": {
"code": "DUPLICATE_EMAIL",
"message": "Email already exists"
}
}
]
},
"meta": {
"total": 2,
"succeeded": 1,
"failed": 1
}
}
When auditing APIs, output findings in this format:
endpoint - [category] Description of issue
Example:
GET /getUsers - [rest] Use noun '/users' instead of verb '/getUsers'
POST /users - [error] Missing validation error details in 400 response
GET /users - [page] Missing pagination metadata in list response
Read individual rule files for detailed explanations:
rules/rest-http-methods.md
rules/error-consistent-format.md
rules/page-cursor-based.md
Weekly Installs
64
Repository
GitHub Stars
11
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex51
gemini-cli50
opencode49
claude-code46
github-copilot43
cursor41
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
159,700 周安装