Communitygithub.com

API 设计模式

REST API 设计模式,包括生产级 API 的资源命名、状态码、分页、过滤、错误响应、版本控制和速率限制。在设计或审查 REST 端点、资源名称、状态码、分页或版本控制时使用。

API 设计模式 是什么?

此技能提供了一套全面的 REST API 设计最佳实践,涵盖从资源命名到高级错误处理等各个方面。它指导开发人员选择正确的 HTTP 方法和状态码,实现高效的分页(基于偏移量和游标),以及设计灵活过滤、排序和搜索参数。该技能还解决了身份验证、授权、速率限制和 API 版本控制等关键主题,并提供了清晰实用的决策指南。通过遵循示例和清单,开发人员可以创建一致、易于使用且可扩展的 API,这些 API 在多个平台上具有良好的性能,并且不会泄露内部细节。

兼容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/api-design

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

API 设计模式 是做什么的?

用于设计一致、开发者友好的 REST API 的惯例和最佳实践。

何时激活

  • 设计新的 API 端点
  • 审查现有的 API 合同
  • 添加分页、过滤或排序
  • 实现 API 的错误处理
  • 规划 API 版本控制策略
  • 构建面向公众或合作伙伴的 API

资源设计

URL 结构

# 资源是名词、复数、小写、kebab-case
GET    /api/v1/users
GET    /api/v1/users/:id
POST   /api/v1/users
PUT    /api/v1/users/:id
PATCH  /api/v1/users/:id
DELETE /api/v1/users/:id

# 用于关系的子资源
GET    /api/v1/users/:id/orders
POST   /api/v1/users/:id/orders

# 不映射到 CRUD 的操作(谨慎使用动词)
POST   /api/v1/orders/:id/cancel
POST   /api/v1/auth/login
POST   /api/v1/auth/refresh

命名规则

# 良好
/api/v1/team-members          # 多词资源使用 kebab-case
/api/v1/orders?status=active  # 用于过滤的查询参数
/api/v1/users/123/orders      # 用于所有权的嵌套资源

# 不佳
/api/v1/getUsers              # URL 中使用动词
/api/v1/user                  # 单数(应使用复数)
/api/v1/team_members          # URL 中使用蛇形命名
/api/v1/users/123/getOrders   # 嵌套资源中使用动词

HTTP 方法和状态码

方法语义

方法幂等安全用途
GET是是检索资源
POST否否创建资源、触发操作
PUT是否完全替换资源
PATCH否*否部分更新资源
DELETE是否删除资源

*PATCH 可以通过适当的实现实现幂等

状态码参考

# 成功
200 OK                    — GET, PUT, PATCH(带有响应体)
201 Created               — POST(包含 Location 头)
204 No Content            — DELETE, PUT(无响应体)

# 客户端错误
400 Bad Request           — 验证失败、JSON 格式错误
401 Unauthorized          — 缺少或无效的身份验证
403 Forbidden             — 已通过身份验证但未授权
404 Not Found             — 资源不存在
409 Conflict              — 重复条目、状态冲突
422 Unprocessable Entity  — 语义上无效(有效的 JSON,错误的数据)
429 Too Many Requests     — 超过速率限制

# 服务器错误
500 Internal Server Error — 意外失败(切勿暴露详细信息)
502 Bad Gateway           — 上游服务失败
503 Service Unavailable   — 临时过载,包含 Retry-After 头

常见错误

# 不佳:200 用于所有情况
{ "status": 200, "success": false, "error": "未找到" }

# 良好:语义化使用 HTTP 状态码
HTTP/1.1 404 Not Found
{ "error": { "code": "not_found", "message": "用户未找到" } }

# 不佳:500 用于验证错误
# 良好:400 或 422,并附带字段级别的详细信息

# 不佳:200 用于已创建的资源
# 良好:201,并附带 Location 头
HTTP/1.1 201 Created
Location: /api/v1/users/abc-123

响应格式

成功响应

{
  "data": {
    "id": "abc-123",
    "email": "[email protected]",
    "name": "爱丽丝",
    "created_at": "2025-01-15T10:30:00Z"
  }
}

集合响应(带分页)

{
  "data": [
    { "id": "abc-123", "name": "爱丽丝" },
    { "id": "def-456", "name": "鲍勃" }
  ],
  "meta": {
    "total": 142,
    "page": 1,
    "per_page": 20,
    "total_pages": 8
  },
  "links": {
    "self": "/api/v1/users?page=1&per_page=20",
    "next": "/api/v1/users?page=2&per_page=20",
    "last": "/api/v1/users?page=8&per_page=20"
  }
}

错误响应

{
  "error": {
    "code": "validation_error",
    "message": "请求验证失败",
    "details": [
      {
        "field": "email",
        "message": "必须是有效的电子邮件地址",
        "code": "invalid_format"
      },
      {
        "field": "age",
        "message": "必须在 0 到 150 之间",
        "code": "out_of_range"
      }
    ]
  }
}

响应包装变体

// 选项 A:带有数据包装器的信封(推荐用于公共 API)
interface ApiResponse<T> {
  data: T;
  meta?: PaginationMeta;
  links?: PaginationLinks;
}

interface ApiError {
  error: {
    code: string;
    message: string;
    details?: FieldError[];
  };
}

// 选项 B:扁平响应(更简单,常用于内部 API)
// 成功:直接返回资源
// 错误:返回错误对象
// 通过 HTTP 状态码区分

分页

基于偏移量(简单)

GET /api/v1/users?page=2&per_page=20

# 实现
SELECT * FROM users
ORDER BY created_at DESC
LIMIT 20 OFFSET 20;

优点: 易于实现,支持“跳转到第 N 页” 缺点: 在大偏移量时性能慢 (OFFSET 100000),并发插入时数据不一致

基于游标(可扩展)

GET /api/v1/users?cursor=eyJpZCI6MTIzfQ&limit=20

# 实现
SELECT * FROM users
WHERE id > :cursor_id
ORDER BY id ASC
LIMIT 21;  -- 多获取一条以确定 has_next
{
  "data": [...],
  "meta": {
    "has_next": true,
    "next_cursor": "eyJpZCI6MTQzfQ"
  }
}

优点: 无论位置如何,性能始终如一,并发插入时稳定 缺点: 无法跳转到任意页面,游标不透明

何时使用哪种

使用场景分页类型
管理仪表板、小数据集(<10K)偏移量
无限滚动、信息流、大数据集游标
公共 API游标(默认)并可选支持偏移量
搜索结果偏移量(用户期望页码)

过滤、排序和搜索

过滤

# 简单相等
GET /api/v1/orders?status=active&customer_id=abc-123

# 比较运算符(使用方括号表示法)
GET /api/v1/products?price[gte]=10&price[lte]=100
GET /api/v1/orders?created_at[after]=2025-01-01

# 多个值(逗号分隔)
GET /api/v1/products?category=electronics,clothing

# 嵌套字段(点表示法)
GET /api/v1/orders?customer.country=US

排序

# 单个字段(前缀 - 表示降序)
GET /api/v1/products?sort=-created_at

# 多个字段(逗号分隔)
GET /api/v1/products?sort=-featured,price,-created_at

全文搜索

# 搜索查询参数
GET /api/v1/products?q=无线+耳机

# 特定字段搜索
GET /api/v1/users?email=alice

稀疏字段集

# 仅返回指定字段(减少负载)
GET /api/v1/users?fields=id,name,email
GET /api/v1/orders?fields=id,total,status&include=customer.name

身份验证和授权

基于令牌的身份验证

# Bearer 令牌在 Authorization 头中
GET /api/v1/users
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

# API 密钥(用于服务器到服务器)
GET /api/v1/data
X-API-Key: sk_live_abc123

授权模式

// 资源级别:检查所有权
app.get("/api/v1/orders/:id", async (req, res) => {
  const order = await Order.findById(req.params.id);
  if (!order) return res.status(404).json({ error: { code: "not_found" } });
  if (order.userId !== req.user.id) return res.status(403).json({ error: { code: "forbidden" } });
  return res.json({ data: order });
});

// 基于角色:检查权限
app.delete("/api/v1/users/:id", requireRole("admin"), async (req, res) => {
  await User.delete(req.params.id);
  return res.status(204).send();
});

速率限制

头部

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

# 超过限制时
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "超过速率限制。请在 60 秒后重试。"
  }
}

速率限制层级

层级限制窗口使用场景
匿名30/分钟每 IP公共端点
已认证100/分钟每用户标准 API 访问
高级1000/分钟每 API 密钥付费 API 计划
内部10000/分钟每服务服务间通信

版本控制

URL 路径版本控制(推荐)

/api/v1/users
/api/v2/users

优点: 明确、易于路由、可缓存 缺点: URL 在不同版本之间变化

头部版本控制

GET /api/users
Accept: application/vnd.myapp.v2+json

优点: URL 干净 缺点: 难以测试、容易忘记

版本控制策略

1. 从 /api/v1/ 开始——在需要之前不要版本化
2. 最多维护两个活跃版本(当前 + 上一个)
3. 弃用时间线:
   - 宣布弃用(公共 API 提前 6 个月通知)
   - 添加 Sunset 头:Sunset: Sat, 01 Jan 2026 00:00:00 GMT
   - 在弃用日期后返回 410 Gone
4. 非破坏性更改不需要新版本:
   - 向响应添加新字段
   - 添加新的可选查询参数
   - 添加新端点
5. 破坏性更改需要新版本:
   - 删除或重命名字段
   - 更改字段类型
   - 更改 URL 结构
   - 更改身份验证方法

实现模式

TypeScript (Next.js API Route)

import { z } from "zod";
import { NextRequest, NextResponse } from "next/server";

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
});

export async function POST(req: NextRequest) {
  const body = await req.json();
  const parsed = createUserSchema.safeParse(body);

  if (!parsed.success) {
    return NextResponse.json({
      error: {
        code: "validation_error",
        message: "请求验证失败",
        details: parsed.error.issues.map(i => ({
          field: i.path.join("."),
          message: i.message,
          code: i.code,
        })),
      },
    }, { status: 422 });
  }

  const user = await createUser(parsed.data);

  return NextResponse.json(
    { data: user },
    {
      status: 201,
      headers: { Location: `/api/v1/users/${user.id}` },
    },
  );
}

Python (Django REST Framework)

from rest_framework import serializers, viewsets, status
from rest_framework.response import Response

class CreateUserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    name = serializers.CharField(max_length=100)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ["id", "email", "name", "created_at"]

class UserViewSet(viewsets.ModelViewSet):
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

    def get_serializer_class(self):
        if self.action == "create":
            return CreateUserSerializer
        return UserSerializer

    def create(self, request):
        serializer = CreateUserSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = UserService.create(**serializer.validated_data)
        return Response(
            {"data": UserSerializer(user).data},
            status=status.HTTP_201_CREATED,
            headers={"Location": f"/api/v1/users/{user.id}"},
        )

Go (net/http)

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        writeError(w, http.StatusBadRequest, "invalid_json", "Invalid request body")
        return
    }

    if err := req.Validate(); err != nil {
        writeError(w, http.StatusUnprocessableEntity, "validation_error", err.Error())
        return
    }

    user, err := h.service.Create(r.Context(), req)
    if err != nil {
        switch {
        case errors.Is(err, domain.ErrEmailTaken):
            writeError(w, http.StatusConflict, "email_taken", "Email already registered")
        default:
            writeError(w, http.StatusInternalServerError, "internal_error", "Internal error")
        }
        return
    }

    w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
    writeJSON(w, http.StatusCreated, map[string]any{"data": user})
}

API 设计清单

在发布新端点之前:

  • 资源 URL 遵循命名约定(复数、kebab-case、无动词)
  • 使用了正确的 HTTP 方法(GET 用于读取,POST 用于创建等)
  • 返回了适当的状态码(不全是 200)
  • 使用模式验证输入(Zod、Pydantic、Bean Validation)
  • 错误响应遵循标准格式,包含代码和消息
  • 为列表端点实现了分页(游标或偏移量)
  • 要求身份验证(或明确标记为公共)
  • 检查了授权(用户只能访问自己的资源)
  • 配置了速率限制
  • 响应不会泄露内部细节(堆栈跟踪、SQL 错误)
  • 与现有端点命名一致(camelCase vs snake_case)
  • 已记录(更新了 OpenAPI/Swagger 规范)

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能