mcpField
A
api-design-principles

api-design-principles

github.com/secondsky开发240472 次安装5 周前更新
代码仓库

关于 api-design-principles

api-design-principles 是用于设计和评审 REST 与 GraphQL API 的技能,涵盖资源和 Schema 设计、版本管理、分页、错误处理,以及可维护且便于开发者使用的实践。

主要功能

  1. 01REST 资源与 HTTP 方法设计
  2. 02GraphQL Schema 与查询模式
  3. 03API 版本管理策略
  4. 04分页与错误处理
  5. 05DataLoader 和 API 文档实践

适用场景

  1. 01规划新的 REST 或 GraphQL API
  2. 02评审 API 规范
  3. 03重构端点或 Schema
  4. 04为团队制定 API 规范

安装

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

$npx skills add https://github.com/secondsky/claude-skills --skill api-design-principles

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

详细介绍

API 设计原则

掌握 REST 和 GraphQL API 设计原则,构建直观、可扩展且易于维护的 API,让开发者满意,并经得起时间考验。

适用场景

  • 设计新的 REST 或 GraphQL API
  • 重构现有 API,提升易用性
  • 为团队制定 API 设计标准
  • 在实现前审查 API 规范
  • 在不同 API 范式之间迁移(例如从 REST 迁移到 GraphQL)
  • 编写方便开发者使用的 API 文档
  • 针对特定用例(移动端、第三方集成)优化 API

核心概念

1. RESTful 设计原则

面向资源的架构

  • 资源是名词(用户、订单、产品),而不是动词
  • 使用 HTTP 方法执行操作(GET、POST、PUT、PATCH、DELETE)
  • URL 表示资源层级
  • 使用一致的命名约定

HTTP 方法语义:

  • GET:获取资源(幂等、安全)
  • POST:创建新资源
  • PUT:替换整个资源(幂等)
  • PATCH:部分更新资源
  • DELETE:删除资源(幂等)
2. GraphQL 设计原则

以 Schema 为先的开发

  • 类型定义领域模型
  • 使用查询读取数据
  • 使用变更修改数据
  • 使用订阅实时更新

查询结构:

  • 客户端只请求所需的数据
  • 单个端点,支持多个操作
  • 强类型 Schema
  • 内置自省功能
3. API 版本控制策略

URL 版本控制:

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

Header 版本控制:

Accept: application/vnd.api+json; version=1

查询参数版本控制:

/api/users?version=1

REST API 设计模式

模式 1:资源集合设计
# Good: Resource-oriented endpoints
GET    /api/users              # List users (with pagination)
POST   /api/users              # Create user
GET    /api/users/{id}         # Get specific user
PUT    /api/users/{id}         # Replace user
PATCH  /api/users/{id}         # Update user fields
DELETE /api/users/{id}         # Delete user

# Nested resources
GET    /api/users/{id}/orders  # Get user's orders
POST   /api/users/{id}/orders  # Create order for user

# Bad: Action-oriented endpoints (avoid)
POST   /api/createUser
POST   /api/getUserById
POST   /api/deleteUser
模式 2:分页和筛选
from fastapi import FastAPI, Query
from pydantic import BaseModel, Field
from typing import List, Optional

class PaginatedResponse(BaseModel):
    items: List[dict]
    total: int
    page: int
    page_size: int
    pages: int

    @property
    def has_next(self) -> bool:
        return self.page < self.pages

app = FastAPI()

@app.get("/api/users", response_model=PaginatedResponse)
async def list_users(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    status: Optional[str] = Query(None),
    search: Optional[str] = Query(None)
):
    query = build_query(status=status, search=search)
    total = await count_users(query)
    offset = (page - 1) * page_size
    users = await fetch_users(query, limit=page_size, offset=offset)

    return PaginatedResponse(
        items=users,
        total=total,
        page=page,
        page_size=page_size,
        pages=(total + page_size - 1) // page_size
    )
模式 3:错误处理和状态码
from fastapi import HTTPException, status

STATUS_CODES = {
    "success": 200,
    "created": 201,
    "no_content": 204,
    "bad_request": 400,
    "unauthorized": 401,
    "forbidden": 403,
    "not_found": 404,
    "conflict": 409,
    "unprocessable": 422,
    "internal_error": 500
}

def raise_not_found(resource: str, id: str):
    raise HTTPException(
        status_code=status.HTTP_404_NOT_FOUND,
        detail={
            "error": "NotFound",
            "message": f"{resource} not found",
            "details": {"id": id}
        }
    )
模式 4:HATEOAS(超媒体即应用状态引擎)
class UserResponse(BaseModel):
    id: str
    name: str
    email: str
    _links: dict

    @classmethod
    def from_user(cls, user: User, base_url: str):
        return cls(
            id=user.id,
            name=user.name,
            email=user.email,
            _links={
                "self": {"href": f"{base_url}/api/users/{user.id}"},
                "orders": {"href": f"{base_url}/api/users/{user.id}/orders"},
                "update": {"href": f"{base_url}/api/users/{user.id}", "method": "PATCH"},
                "delete": {"href": f"{base_url}/api/users/{user.id}", "method": "DELETE"}
            }
        )

GraphQL 设计模式

模式 1:Schema 设计
type User {
  id: ID!
  email: String!
  name: String!
  orders(first: Int = 20, after: String): OrderConnection!
}

type OrderConnection {
  edges: [OrderEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload!
}

input CreateUserInput {
  email: String!
  name: String!
  password: String!
}

type CreateUserPayload {
  user: User
  errors: [Error!]
}
模式 2:DataLoader(防止 N+1 问题)
from aiodataloader import DataLoader

class UserLoader(DataLoader):
    async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:
        users = await fetch_users_by_ids(user_ids)
        user_map = {user["id"]: user for user in users}
        return [user_map.get(user_id) for user_id in user_ids]

最佳实践

REST API
  1. 命名一致:集合使用复数名词
  2. 无状态:每个请求都包含所有必要信息
  3. 正确使用 HTTP 状态码:2xx 表示成功,4xx 表示客户端错误,5xx 表示服务器错误
  4. 为 API 进行版本控制:从一开始就为不兼容的变更做好规划
  5. 分页:始终对大型集合进行分页
  6. 速率限制:通过速率限制保护 API
  7. 文档:使用 OpenAPI/Swagger 编写交互式文档
GraphQL API
  1. 以 Schema 为先:在编写解析器前设计 Schema
  2. 避免 N+1 问题:使用 DataLoader 高效获取数据
  3. 输入验证:在 Schema 和解析器层进行验证
  4. 错误处理:在变更载荷中返回结构化错误
  5. 分页:使用基于游标的分页(Relay 规范)
  6. 弃用:使用 @deprecated 指令逐步迁移
  7. 监控:跟踪查询复杂度和执行时间

常见陷阱

  • 过度获取/获取不足(REST):GraphQL 可以解决此问题,但需要使用 DataLoader
  • 不兼容的变更:为 API 进行版本控制,或采用弃用策略
  • 错误格式不一致:统一错误响应格式
  • 缺少速率限制:不设限制的 API 容易遭到滥用
  • 文档不完善:缺少文档的 API 会让开发者感到困扰
  • 忽略 HTTP 语义:对幂等操作使用 POST 会违背预期
  • 紧密耦合:API 结构不应照搬数据库 Schema

评论

支持 Markdown