API 接口构建器
api-endpoint-builder
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- 数据建模
局限性
- 仅在任务明确符合上述范围时使用此技能。
- 不要将输出视为针对特定环境的验证、测试或专家审查的替代方案。
- 如果缺少必要的输入、权限、安全边界或验收标准,请停止并请求澄清。