何时使用
适用:
- 用户要求「创建一个 API 端点 / 写个 REST 接口」。
- 开发新后端功能、给已有 API 增加端点、实现 CRUD。
- 出现「API、端点、路由、route、REST、接口」等词。
不该用(负边界):
- 仅前端调接口、写客户端 SDK / fetch 封装。
- GraphQL、gRPC、WebSocket、消息队列等非 REST 范式。
- 纯架构/技术选型讨论,不落到具体端点代码。
- 缺少必要输入(数据模型、权限边界、成功标准)时,先停下来问清,不要凭空编。
步骤
每个端点按以下分层逐项产出,缺一不可:
- 路由定义:选对 HTTP 方法,挂上鉴权与校验中间件。
- 入参校验:处理前先校验 body / params / query,不通过即返回 400。
- 鉴权与授权:受保护路由要求登录(401),并校验资源归属(403)。
- 业务逻辑:核心处理,注意幂等与并发冲突(409)。
- 错误处理:try/catch 局部兜底 + 全局错误中间件,生产环境不泄露细节。
- 响应格式化:统一
{ success, data }/{ error }结构,列表带pagination。 - API 文档:用 JSDoc 注释标注路由、入参、返回码、示例。
- 测试(按需):覆盖正常路径与关键校验失败路径。
指令
路由 + 校验 + 处理函数三段式(Express / Fastify 任选其一):
// Express
router.post('/api/users', authenticate, validateUser, createUser);
// Fastify(用 schema 内建校验)
fastify.post('/api/users', { preHandler: [authenticate], schema: userSchema }, createUser);
入参校验中间件,处理前先拦截非法输入:
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();
};
处理函数:查重(409) → 加盐哈希密码 → 落库 → 剔除敏感字段 → 返回 201:
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' });
}
};
全局错误中间件,生产环境屏蔽内部细节:
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 });
});
HTTP 状态码约定:
200成功(GET/PUT/PATCH)|201已创建(POST)|204无内容(DELETE)400校验失败|401未认证|403无权限|404未找到|409冲突/重复|500服务端错误
统一响应结构:
// 成功
{ "success": true, "data": { } }
// 错误
{ "error": "Error message", "details": { } }
// 列表带分页
{ "success": true, "data": [], "pagination": { "page": 1, "limit": 20, "total": 100 } }
安全检查清单(上线前逐项确认):
- 受保护路由强制认证;校验资源归属(用户只能动自己的数据)
- 所有字段入参校验;用参数化查询防 SQL 注入
- 公开端点加限流;设置请求体大小上限
- 响应不含敏感数据(密码、token);CORS 正确配置
示例
CRUD 路由约定:
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 删除
分页查询(并发取数据与总数):
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) } });
};
过滤与排序:
const { status, sort = '-createdAt' } = req.query;
const filter = {};
if (status) filter.status = status;
const resources = await db.resources.find(filter).sort(sort).limit(20);
测试(覆盖成功 + 校验失败两条路径):
describe('POST /api/users', () => {
it('creates a user', async () => {
const res = await request(app).post('/api/users')
.send({ email: 'test@example.com', name: 'Test User', password: 'password123' });
expect(res.status).toBe(201);
expect(res.body.success).toBe(true);
expect(res.body.data.password).toBeUndefined(); // 不回传密码
});
it('rejects invalid email', async () => {
const res = await request(app).post('/api/users')
.send({ email: 'invalid', name: 'Test User', password: 'password123' });
expect(res.status).toBe(400);
expect(res.body.error).toContain('email');
});
});
文档注释模板:
/**
* @route POST /api/users
* @desc 创建新用户
* @access Public
* @body {string} email 用户邮箱(必填)
* @body {string} name 用户名(必填)
* @body {string} password 密码,至少 8 位(必填)
* @returns {201} 创建成功 / {400} 校验失败 / {409} 已存在 / {500} 服务端错误
*/
注意事项
- 核心原则:处理前先校验入参;用对状态码;优雅处理错误;绝不暴露敏感数据;响应结构统一;该鉴权处必鉴权;端点写文档;关键路径写测试。
- 本技能产出是脚手架,不能替代针对你实际环境的校验、测试与专家评审。
- 仅在任务确实落到「具体 REST 端点实现」时使用;范围不符请勿套用。
互见
安全审计 / security-auditor:上线前安全评审。测试驱动开发 / test-driven-development:补齐测试。数据库设计 / database-design:数据建模与表结构。
采编自 sickn33/antigravity-awesome-skills(MIT 许可证)。