API设计Skill api-design

API设计技能用于设计和构建清洁、可扩展、易于维护的REST和GraphQL API,涵盖端点规划、版本控制、认证授权、文档化、性能优化等关键方面,适用于后端开发和微服务架构。关键词:API设计,REST API,GraphQL,后端开发,架构设计,微服务,认证授权,版本控制,API文档。

架构设计 0 次安装 0 次浏览 更新于 3/18/2026

name: api-design 描述:遵循行业最佳实践设计清洁、可扩展和维护的REST和GraphQL API。用于设计公共或内部API、规划端点结构、定义请求/响应合约、建立版本控制策略、实现认证模式、设计数据模型、创建API文档、确保一致的错误处理、优化性能或建立微服务之间的服务合约。

API 设计 - 构建清洁、可扩展的 REST & GraphQL API

何时使用此技能

  • 从零开始设计新的REST或GraphQL API
  • 规划端点结构和URL模式
  • 定义请求/响应合约和数据模式
  • 建立API版本控制和弃用策略
  • 实现认证和授权模式
  • 创建API文档(OpenAPI/Swagger)
  • 设计错误响应格式和状态码
  • 规划分页、过滤和排序策略
  • 建立速率限制和节流策略
  • 设计Webhooks或事件驱动集成
  • 创建微服务的服务合约
  • 优化API性能和缓存策略

何时使用此技能

  • 设计公共或内部API,规划端点,定义服务之间的合约。
  • 当处理相关任务或功能时
  • 在需要此专业知识的开发期间

使用时机:设计公共或内部API,规划端点,定义服务之间的合约。

核心原则

  1. 一致性 - 跨端点的可预测模式
  2. 简洁性 - 易于理解和使用
  3. 版本控制 - 支持进化而不破坏客户端
  4. 安全性 - 认证、授权、速率限制
  5. 文档化 - 清晰、最新的API规范

REST API 设计

1. 基于资源的URLs

✅ 好 - 使用名词,而非动词
GET    /users              - 列出用户
GET    /users/:id          - 获取特定用户
POST   /users              - 创建用户
PUT    /users/:id          - 更新用户(完全替换)
PATCH  /users/:id          - 更新用户(部分)
DELETE /users/:id          - 删除用户

GET    /users/:id/posts    - 获取用户的帖子
POST   /users/:id/posts    - 为用户创建帖子

❌ 不好 - URL中使用动词
GET    /getUsers
POST   /createUser
POST   /users/delete/:id

2. HTTP 方法与状态码

// ✅ 正确的HTTP方法使用
app.get('/users', async (req, res) => {
  const users = await db.users.findAll();
  res.json(users); // 200 OK
});

app.post('/users', async (req, res) => {
  const user = await db.users.create(req.body);
  res.status(201) // 201 已创建
     .location(`/users/${user.id}`)
     .json(user);
});

app.put('/users/:id', async (req, res) => {
  const user = await db.users.update(req.params.id, req.body);
  if (!user) {
    return res.status(404).json({ error: '用户未找到' });
  }
  res.json(user); // 200 OK
});

app.delete('/users/:id', async (req, res) => {
  await db.users.delete(req.params.id);
  res.status(204).send(); // 204 无内容
});

// ✅ 常见状态码
// 2xx 成功
200 OK                  - 请求成功
201 Created             - 资源已创建
204 No Content          - 成功,无响应体

// 4xx 客户端错误
400 Bad Request         - 无效输入
401 Unauthorized        - 未认证
403 Forbidden           - 已认证但无权限
404 Not Found           - 资源不存在
409 Conflict            - 重复或冲突状态
422 Unprocessable       - 验证失败
429 Too Many Requests   - 速率限制

// 5xx 服务器错误
500 Internal Error      - 服务器内部错误
503 Service Unavailable - 暂时不可用

3. 请求/响应格式

// ✅ 一致的响应结构
interface ApiResponse<T> {
  data: T;
  meta?: {
    page?: number;
    limit?: number;
    total?: number;
  };
  links?: {
    self: string;
    next?: string;
    prev?: string;
  };
}

// 成功响应
{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "john@example.com"
  }
}

// 列表响应与分页
{
  "data": [
    { "id": "1", "name": "用户 1" },
    { "id": "2", "name": "用户 2" }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 100
  },
  "links": {
    "self": "/users?page=1",
    "next": "/users?page=2"
  }
}

// 错误响应
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "邮箱格式无效",
    "details": [
      {
        "field": "email",
        "message": "必须为有效邮箱"
      }
    ]
  }
}

4. 过滤、排序、分页

// ✅ 查询参数用于过滤
GET /users?status=active&role=admin
GET /posts?author=john&tags=tech,programming
GET /products?minPrice=10&maxPrice=100

app.get('/users', async (req, res) => {
  const { status, role, page = 1, limit = 20, sort = 'createdAt' } = req.query;
  
  const query = {};
  if (status) query.status = status;
  if (role) query.role = role;
  
  const users = await db.users.findMany({
    where: query,
    skip: (page - 1) * limit,
    take: limit,
    orderBy: { [sort]: 'desc' }
  });
  
  const total = await db.users.count({ where: query });
  
  res.json({
    data: users,
    meta: { page, limit, total },
    links: {
      self: `/users?page=${page}`,
      next: page * limit < total ? `/users?page=${page + 1}` : null
    }
  });
});

// ✅ 排序
GET /users?sort=name           - 按名称升序排序
GET /users?sort=-createdAt     - 按创建时间降序排序
GET /users?sort=role,-createdAt - 多字段排序

// ✅ 字段选择(稀疏字段集)
GET /users?fields=id,name,email - 仅返回指定字段

5. 版本控制

// ✅ URL版本控制(最常见)
GET /api/v1/users
GET /api/v2/users

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// ✅ 头部版本控制
GET /api/users
Headers: { "Accept-Version": "v2" }

// ✅ 弃用警告
app.use('/api/v1', (req, res, next) => {
  res.set('X-API-Deprecation', 'v1 将于 2024-12-31 弃用');
  res.set('X-API-Upgrade', '查看 /api/v2 获取最新版本');
  next();
});

6. 认证与授权

// ✅ Bearer 令牌认证
GET /api/users
Headers: { "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }

function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).json({ error: '未提供令牌' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: '无效令牌' });
  }
}

// ✅ 速率限制
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 分钟
  max: 100
});

app.use('/api/', limiter);

// ✅ 服务间 API 密钥
GET /api/users
Headers: { "X-API-Key": "sk_live_abc123..." }

function requireApiKey(req, res, next) {
  const apiKey = req.get('X-API-Key');
  
  if (!isValidApiKey(apiKey)) {
    return res.status(401).json({ error: '无效 API 密钥' });
  }
  next();
}

7. HATEOAS(超媒体)

// ✅ 包含相关资源链接
{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "john@example.com"
  },
  "links": {
    "self": "/users/123",
    "posts": "/users/123/posts",
    "followers": "/users/123/followers"
  }
}

// ✅ 状态转换的操作链接
{
  "data": {
    "id": "456",
    "status": "pending",
    "amount": 100
  },
  "actions": {
    "approve": {
      "method": "POST",
      "href": "/orders/456/approve"
    },
    "cancel": {
      "method": "DELETE",
      "href": "/orders/456"
    }
  }
}

GraphQL API 设计

1. 模式定义

# ✅ 清晰、类型化的模式
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
  createdAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  published: Boolean!
}

type Query {
  user(id: ID!): User
  users(limit: Int, offset: Int): [User!]!
  post(id: ID!): Post
  posts(authorId: ID, published: Boolean): [Post!]!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
  
  createPost(input: CreatePostInput!): Post!
  publishPost(id: ID!): Post!
}

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

input UpdateUserInput {
  name: String
  email: String
}

input CreatePostInput {
  title: String!
  content: String!
  authorId: ID!
}

2. 解析器

// ✅ 高效的解析器与 DataLoader
import DataLoader from 'dataloader';

const userLoader = new DataLoader(async (ids) => {
  const users = await db.users.findMany({
    where: { id: { in: ids } }
  });
  
  // 按输入 ID 顺序返回
  return ids.map(id => users.find(u => u.id === id));
});

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      return await userLoader.load(id);
    },
    
    users: async (_, { limit = 20, offset = 0 }) => {
      return await db.users.findMany({
        take: limit,
        skip: offset
      });
    }
  },
  
  Mutation: {
    createUser: async (_, { input }) => {
      return await db.users.create(input);
    },
    
    updateUser: async (_, { id, input }) => {
      return await db.users.update(id, input);
    }
  },
  
  User: {
    // User.posts 字段的解析器
    posts: async (user) => {
      return await db.posts.findMany({
        where: { authorId: user.id }
      });
    }
  }
};

3. 错误处理

// ✅ GraphQL 错误处理
import { GraphQLError } from 'graphql';

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      const user = await db.users.findById(id);
      
      if (!user) {
        throw new GraphQLError('用户未找到', {
          extensions: {
            code: 'USER_NOT_FOUND',
            id
          }
        });
      }
      
      return user;
    }
  },
  
  Mutation: {
    createUser: async (_, { input }) => {
      try {
        return await db.users.create(input);
      } catch (error) {
        if (error.code === 'P2002') { // 唯一约束
          throw new GraphQLError('邮箱已存在', {
            extensions: {
              code: 'DUPLICATE_EMAIL',
              field: 'email'
            }
          });
        }
        throw error;
      }
    }
  }
};

API 文档

1. OpenAPI/Swagger (REST)

// ✅ OpenAPI 规范
/**
 * @swagger
 * /users:
 *   get:
 *     summary: 列出所有用户
 *     tags: [Users]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *         description: 页码
 *     responses:
 *       200:
 *         description: 成功
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/User'
 */
app.get('/users', async (req, res) => {
  // 实现
});

// components/schemas/User
/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       required:
 *         - id
 *         - email
 *       properties:
 *         id:
 *           type: string
 *           example: "123"
 *         name:
 *           type: string
 *           example: "John Doe"
 *         email:
 *           type: string
 *           format: email
 *           example: "john@example.com"
 */

2. GraphQL 文档

# ✅ 模式中的描述(自动文档化)
"""
表示系统中的用户
"""
type User {
  """唯一标识符"""
  id: ID!
  
  """用户全名"""
  name: String!
  
  """用户邮箱地址"""
  email: String!
  
  """此用户创作的帖子"""
  posts: [Post!]!
}

"""
创建新用户
"""
createUser(
  """用户详情"""
  input: CreateUserInput!
): User!

API 最佳实践

1. 幂等性

// ✅ 幂等操作(可安全重试)
app.put('/users/:id', async (req, res) => {
  // PUT 是幂等的 - 重复结果相同
  const user = await db.users.upsert({
    where: { id: req.params.id },
    create: req.body,
    update: req.body
  });
  res.json(user);
});

// ✅ POST 的幂等键
app.post('/payments', async (req, res) => {
  const idempotencyKey = req.get('Idempotency-Key');
  
  if (!idempotencyKey) {
    return res.status(400).json({ error: '需要 Idempotency-Key' });
  }
  
  // 检查是否已处理
  const existing = await cache.get(`payment:${idempotencyKey}`);
  if (existing) {
    return res.json(existing); // 返回缓存结果
  }
  
  const payment = await processPayment(req.body);
  await cache.set(`payment:${idempotencyKey}`, payment, 24 * 60 * 60);
  
  res.status(201).json(payment);
});

2. 缓存

// ✅ HTTP 缓存头部
app.get('/products/:id', async (req, res) => {
  const product = await db.products.findById(req.params.id);
  
  // 从内容生成 ETag
  const etag = generateETag(product);
  
  // 检查客户端是否有当前版本
  if (req.get('If-None-Match') === etag) {
    return res.status(304).send(); // 未修改
  }
  
  res.set({
    'ETag': etag,
    'Cache-Control': 'public, max-age=300', // 5 分钟
    'Last-Modified': product.updatedAt.toUTCString()
  });
  
  res.json(product);
});

3. Webhooks

// ✅ 用于异步事件的 Webhook 系统
interface WebhookPayload {
  event: string;
  data: any;
  timestamp: string;
  signature: string; // 用于验证的 HMAC
}

async function sendWebhook(url: string, event: string, data: any) {
  const payload = {
    event,
    data,
    timestamp: new Date().toISOString()
  };
  
  // 签名负载
  const signature = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Webhook-Signature': signature
    },
    body: JSON.stringify({ ...payload, signature })
  });
}

// 在事件上触发 webhooks
app.post('/users', async (req, res) => {
  const user = await db.users.create(req.body);
  
  // 异步发送 webhook
  sendWebhook(
    'https://customer.com/webhooks',
    'user.created',
    user
  ).catch(console.error);
  
  res.status(201).json(user);
});

API 设计检查清单

REST:
□ 基于资源的 URLs(名词,非动词)
□ 正确的 HTTP 方法和状态码
□ 一致的响应格式
□ 列表的分页
□ 过滤、排序、字段选择
□ API 版本控制策略
□ 认证(Bearer 令牌、API 密钥)
□ 配置速率限制
□ 正确配置 CORS
□ 标准化的错误响应

GraphQL:
□ 清晰、类型化的模式
□ 高效的解析器(DataLoader)
□ 实现分页(游标或偏移)
□ 带代码的错误处理
□ 认证与授权
□ 查询复杂度限制
□ 深度限制
□ 生产中禁用自省

文档:
□ 发布 OpenAPI/GraphQL 模式
□ 示例请求/响应
□ 认证文档
□ 错误代码文档化
□ 维护变更日志
□ 重大更改的迁移指南

性能:
□ 优化的数据库查询
□ 消除 N+1 查询
□ 响应缓存
□ 启用压缩(gzip)
□ 静态响应的 CDN

安全:
□ 所有端点的输入验证
□ 预防 SQL 注入
□ 每个端点的速率限制
□ 支持 API 密钥轮换
□ 敏感操作的审计日志

资源


记住:优秀的 API 是可预测、文档完善且易于使用的。为 API 消费者设计,而不仅仅是实现。