API 接口构建器

api-endpoint-builder
分类编程
作者Agentic Awesome Skills 社区
许可MIT
评分4.80/5
使用10.2K

API 接口构建器 (API Endpoint Builder)

构建完整且生产就绪的 REST API 接口,包含完善的验证、错误处理、身份验证和文档。

何时使用此技能

  • 用户要求“创建 API 接口”或“构建 REST API”
  • 构建新的后端功能
  • 为现有 API 添加接口
  • 用户提到 “API”、“endpoint”、“route” 或 “REST”
  • 创建 CRUD 操作

你将构建的内容

针对每个接口,你需要创建:

  • 具有正确 HTTP 方法的路由处理器

  • 输入验证(请求体、参数、查询字符串)

  • 身份验证/权限检查

  • 业务逻辑

  • 错误处理

  • 响应格式化

  • API 文档

  • 测试(如果用户要求)

接口结构

1. 路由定义

javascript
// Express 示例
router.post('/api/users', authenticate, validateUser, createUser);

// Fastify 示例
fastify.post('/api/users', {
preHandler: [authenticate],
schema: userSchema
}, createUser);

2. 输入验证

在处理之前务必进行验证:

javascript
const validateUser = (req, res, next) => {
  const { email, name, password } = req.body;
  
  if (!email || !email.includes('@')) {
    return res.status(400).json({ error: 'Valid email required' });
  }
  
  if (!name || name.length < 2) {
    return res.status(400).json({ error: 'Name must be at least 2 characters' });
  }
  
  if (!password || password.length < 8) {
    return res.status(400).json({ error: 'Password must be at least 8 characters' });
  }
  
  next();
};

3. 处理器实现

javascript
const createUser = async (req, res) => {
  try {
    const { email, name, password } = req.body;
    
    // 检查用户是否已存在
    const existing = await db.users.findOne({ email });
    if (existing) {
      return res.status(409).json({ error: 'User already exists' });
    }
    
    // 哈希密码
    const hashedPassword = await bcrypt.hash(password, 10);
    
    // 创建用户
    const user = await db.users.create({
      email,
      name,
      password: hashedPassword,
      createdAt: new Date()
    });
    
    // 不要返回密码
    const { password: _, ...userWithoutPassword } = user;
    
    res.status(201).json({
      success: true,
      data: userWithoutPassword
    });
    
  } catch (error) {
    console.error('Create user error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
};

最佳实践

HTTP 状态码

  • 200 - 成功 (GET, PUT, PATCH)
  • 201 - 已创建 (POST)
  • 204 - 无内容 (DELETE)
  • 400 - 错误请求 (验证失败)
  • 401 - 未授权 (未通过身份验证)
  • 403 - 禁止访问 (无权限)
  • 404 - 未找到
  • 409 - 冲突 (重复)
  • 500 - 内部服务器错误

响应格式

保持结构一致:

javascript
// 成功
{
  "success": true,
  "data": { ... }
}

// 错误
{
"error": "Error message",
"details": { ... } // 可选
}

// 带分页的列表
{
"success": true,
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total": 100
}
}

安全检查清单

  • [ ] 受保护的路由需要身份验证
  • [ ] 权限检查(用户是否拥有该资源)
  • [ ] 输入验证
针对所有字段
  • [ ] 防止 SQL 注入(使用参数化查询)
  • [ ] 对公共端点实施速率限制 (Rate limiting)
  • [ ] 响应中不包含敏感数据(如密码、Token)
  • [ ] 正确配置 CORS
  • [ ] 设置请求大小限制

错误处理

javascript
// 集中式错误处理器
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  // 在生产环境中不要泄露错误详情
  const message = process.env.NODE_ENV === 'production' 
    ? 'Internal server error' 
    : err.message;
  
  res.status(err.status || 500).json({ error: message });
});

常见模式

CRUD 操作

javascript
// 创建
POST /api/resources
Body: { name, description }

// 读取 (列表)
GET /api/resources?page=1&limit=20

// 读取 (单个)
GET /api/resources/:id

// 更新
PUT /api/resources/:id
Body: { name, description }

// 删除
DELETE /api/resources/:id

分页

javascript
const getResources = async (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 20;
  const skip = (page - 1) * limit;
  
  const [resources, total] = await Promise.all([
    db.resources.find().skip(skip).limit(limit),
    db.resources.countDocuments()
  ]);
  
  res.json({
    success: true,
    data: resources,
    pagination: {
      page,
      limit,
      total,
      pages: Math.ceil(total / limit)
    }
  });
};

过滤与排序

javascript
const getResources = async (req, res) => {
  const { status, sort = '-createdAt' } = req.query;
  
  const filter = {};
  if (status) filter.status = status;
  
  const resources = await db.resources
    .find(filter)
    .sort(sort)
    .limit(20);
  
  res.json({ success: true, data: resources });
};

文档模板

javascript
/**
 * @route POST /api/users
 * @desc 创建新用户
 * @access 公开 (Public)
 * 
 * @body {string} email - 用户邮箱 (必填)
 * @body {string} name - 用户姓名 (必填)
 * @body {string} password - 密码,最少 8 位 (必填)
 * 
 * @returns {201} 用户创建成功
 * @returns {400} 验证错误
 * @returns {409} 用户已存在
 * @returns {500} 服务器错误
 * 
 * @example
 * POST /api/users
 * {
 *   "email": "[email protected]",
 *   "name": "John Doe",
 *   "password": "securepass123"
 * }
 */

测试示例

javascript
describe('POST /api/users', () => {
  it('应该创建新用户', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        email: '[email protected]',
        name: 'Test User',
        password: 'password123'
      });
    
    expect(response.status).toBe(201);
    expect(response.body.success).toBe(true);
    expect(response.body.data.email).toBe('[email protected]');
    expect(response.body.data.password).toBeUndefined();
  });
  
  it('应该拒绝无效的邮箱', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        email: 'invalid',
        name: 'Test User',
        password: 'password123'
      });
    
    expect(response.status).toBe(400);
    expect(response.body.error).toContain('email');
  });
});

核心原则

  • 在处理前验证所有输入
  • 使用正确的 HTTP 状态码
  • 优雅地处理错误
  • 绝不泄露敏感数据
  • 保持响应格式一致
  • 在必要处添加身份验证
  • 为端点编写文档
  • 为关键路径编写测试

相关技能

  • @security-auditor - 安全审计
审查
  • @test-driven-development - 测试
  • @database-design - 数据建模

局限性

  • 仅在任务明确符合上述范围时使用此技能。
  • 不要将输出视为针对特定环境的验证、测试或专家审查的替代方案。
  • 如果缺少必要的输入、权限、安全边界或验收标准,请停止并请求澄清。