
纸上谈兵的时候,一切都显得很完美:
然后一上线,傻眼了:
这时候才明白一个扎心的真相:
REST API 设计不是为了优雅——而是为了在变化中活下来。

你设计的不是 endpoints,你设计的是不确定性下的契约。
真正影响你 API 设计的因素是什么:
忽视这些 = 脆弱的 API,一扩就崩。踩过坑的都懂。

人人都能跟你聊 /users 和 /orders。
但这只是表面功夫。
真正要问的问题是:
你的资源生命周期是什么?
POST /orders
GET /orders/:id
PUT /orders/:id
DELETE /orders/:id
看起来没问题。实际上对真实业务来说是错的。
为什么?
更好做法:
POST /orders
POST /orders/:id/pay
POST /orders/:id/ship
POST /orders/:id/cancel
这样做的好处:
大多数 API 在重试面前一败涂地。
现实是:
如果你的 endpoint 不具备幂等性 → 重复操作。
支付 API:
POST /payments
客户端超时 → 重试 → 重复扣款。
恭喜,用户信任清零。
POST /payments
Idempotency-Key: 8f3a-xyz-123
服务端逻辑:
if (exists(idempotencyKey)) {
return previousResponse;
}
processPayment();
storeResult(idempotencyKey);
你的 API 调用了:
其中一个挂了。
然后呢?
大多数 API:
返回 500,然后祈祷。
这不是策略,这是玄学。
示例:
{
"status": "partial_success",
"data": {...},
"failed_dependencies": ["inventory-service"]
}
naive 做法:
/v1/users
/v2/users
问题:
即使这样,也推荐:
基于 Header 的版本控制
Accept: application/vnd.myapi.v2+json

经典的 REST 问题。
GET /users/:id
返回:
但客户端只需要 Name。
浪费:带宽 + 延迟。
客户端需要:
要发 3 次请求。
延迟直接翻倍。
GET /users/:id?include=orders,payments
权衡:

const express = require('express');
const app = express();
// Middleware: request ID for tracing
app.use((req, res, next) => {
req.id = crypto.randomUUID();
next();
});
// Idempotency middleware
const store = new Map();
app.post('/payments', async (req, res) => {
const key = req.headers['idempotency-key'];
if (store.has(key)) {
return res.json(store.get(key));
}
const result = await processPayment(req.body);
store.set(key, result);
res.json(result);
});
// Explicit state transition
app.post('/orders/:id/ship', async (req, res) => {
const order = await getOrder(req.params.id);
if (order.status !== 'paid') {
return res.status(400).json({ error: 'Invalid state' });
}
await shipOrder(order);
res.json({ status: 'shipped' });
});
你忽略了:
你的系统本来运行得好好的……直到网络抖动来临。
没有:
调试全靠猜。
改数据库 → API 挂。
修复:
API 是一份契约,不是数据库的镜像
有人这样做:
200 OK (body 里藏着 error)
或者:
500 for everything
两种都是错的。

如果你的 API:
那它就不是生产就绪的。
如果你设计 API 时假设一切都会正常工作,那你的系统在它不正常的那一刻就会崩溃。