mcpField
A
api-endpoint-builder

api-endpoint-builder

github.com/sickn33开发48.7k187 次安装今天更新
代码仓库

关于 api-endpoint-builder

面向后端开发者的 REST API 端点搭建技能,帮助创建便于审查的路由。内容涵盖输入校验、权限检查、错误处理、响应格式、文档及可选测试。

主要功能

  1. 01路由处理器脚手架
  2. 02请求输入校验
  3. 03身份验证与授权检查
  4. 04一致的错误与响应格式
  5. 05端点文档和测试

适用场景

  1. 01为 API 添加 CRUD 端点
  2. 02校验请求正文和参数
  3. 03通过权限检查保护路由
  4. 04编写关键路由的文档与测试

安装

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

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill api-endpoint-builder

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

附带文件README.md

详细介绍

API 端点构建器

构建完整且便于审查的 REST API 端点脚手架,并配备适当的验证、错误处理、身份验证和文档。

何时使用此技能

  • 用户要求“创建 API 端点”或“构建 REST API”
  • 构建新的后端功能
  • 为现有 API 添加端点
  • 用户提到“API”“端点”“路由”或“REST”
  • 创建 CRUD 操作

你将构建的内容

对于每个端点,你将创建:

  • 配备适当 HTTP 方法的路由处理器
  • 输入验证(请求正文、参数、查询)
  • 身份验证/授权检查
  • 业务逻辑
  • 错误处理
  • 响应格式
  • API 文档
  • 测试(如有要求)

端点结构

1. 路由定义
// Express example
router.post('/api/users', authenticate, validateUser, createUser);

// Fastify example
fastify.post('/api/users', {
  preHandler: [authenticate],
  schema: userSchema
}, createUser);
2. 输入验证

始终先验证,再进行处理:

const validateUser = (req, res, next) => {
  const { email, name, password } = req.body ?? {};
  
  if (typeof email !== 'string' || !email.includes('@')) {
    return res.status(400).json({ error: 'Valid email required' });
  }
  
  if (typeof name !== 'string' || name.trim().length < 2) {
    return res.status(400).json({ error: 'Name must be at least 2 characters' });
  }
  
  if (typeof password !== 'string' || password.length < 8) {
    return res.status(400).json({ error: 'Password must be at least 8 characters' });
  }
  
  next();
};
3. 处理器实现
const createUser = async (req, res) => {
  try {
    const { email, name, password } = req.body ?? {};
    
    // Check if user exists
    const existing = await db.users.findOne({ email });
    if (existing) {
      return res.status(409).json({ error: 'User already exists' });
    }
    
    // Hash password
    const hashedPassword = await bcrypt.hash(password, 10);
    
    // Create user
    const user = await db.users.create({
      email,
      name,
      password: hashedPassword,
      createdAt: new Date()
    });
    
    // Don't return password
    const userWithoutPassword = { id: user.id, email: user.email, name: user.name };
    
    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 - 内部服务器错误
响应格式

保持结构一致:

// Success
{
  "success": true,
  "data": { ... }
}

// Error
{
  "error": "Error message",
  "details": { ... } // optional
}

// List with pagination
{
  "success": true,
  "data": [...],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 100
  }
}
安全检查清单
  • 受保护的路由需要身份验证
  • 执行授权检查(用户拥有该资源)
  • 验证所有字段的输入
  • 防止 SQL 注入(使用参数化查询)
  • 对公开端点进行速率限制
  • 响应中不包含敏感数据(密码、令牌)
  • 正确配置 CORS
  • 设置请求大小限制
错误处理
// Centralized error handler
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  // Don't leak error details in production
  const message = process.env.NODE_ENV === 'production' 
    ? 'Internal server error' 
    : err.message;
  
  res.status(err.status || 500).json({ error: message });
});

常见模式

CRUD 操作
// Create
POST /api/resources
Body: { name, description }

// Read (list)
GET /api/resources?page=1&limit=20

// Read (single)
GET /api/resources/:id

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

// Delete
DELETE /api/resources/:id
分页
const getResources = async (req, res) => {
  const page = Number(req.query.page ?? 1);
  const limit = Number(req.query.limit ?? 20);
  if (!Number.isSafeInteger(page) || page < 1 || !Number.isSafeInteger(limit) || limit < 1 || limit > 100 || (page - 1) * limit > 100000) {
    return res.status(400).json({ error: "Invalid pagination" });
  }
  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)
    }
  });
};
筛选与排序
const getResources = async (req, res) => {
  const { status, sort = '-createdAt' } = req.query;
  
  const filter = {};
  if (status && !["active", "inactive"].includes(status)) return res.status(400).json({ error: "Invalid status" });
  if (!["createdAt", "-createdAt", "name", "-name"].includes(sort)) return res.status(400).json({ error: "Invalid sort" });
  if (status) filter.status = status;
  
  const resources = await db.resources
    .find(filter)
    .sort(sort)
    .limit(20);
  
  res.json({ success: true, data: resources });
};

文档模板

/**
 * @route POST /api/users
 * @desc Create a new user
 * @access Public
 * 
 * @body {string} email - User email (required)
 * @body {string} name - User name (required)
 * @body {string} password - Password, min 8 chars (required)
 * 
 * @returns {201} User created successfully
 * @returns {400} Validation error
 * @returns {409} User already exists
 * @returns {500} Server error
 * 
 * @example
 * POST /api/users
 * {
 *   "email": "[email protected]",
 *   "name": "John Doe",
 *   "password": "securepass123"
 * }
 */

测试示例

describe('POST /api/users', () => {
  it('should create a new user', 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('should reject invalid email', 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 - 数据建模

限制

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

这些片段需要实际的数据库/模型、身份验证中间件和应用设置。应使用架构验证电子邮件/密码长度和字节数限制,而不能只依赖示例中的 @ 检查。在数据库中强制执行规范化电子邮件的唯一性,并将重复键竞争映射为 409;预先检查并不足够。对行查询和计数查询都应用经过身份验证的租户/资源筛选条件;将响应字段以及排序/筛选输入限制在允许列表内,并对错误日志进行净化。上线前,测试非字符串请求正文、授权和并发重复项。

评论

支持 Markdown