API 设计之道:用 PHP 构建符合 RESTful 与 GraphQL 规范的高可用接口
在微服务、前后端分离与多端协同成为主流的今天,API 已不再是"附属产物",而是系统的核心契约。
PHP 作为最成熟的后端语言之一,在 API 领域依然拥有极强生命力:Laravel、Symfony、Hyperf、ThinkPHP 都在 API 设计上投入了大量工程实践。
本文将围绕 RESTful 与 GraphQL 两条主线 ,系统讲解如何用 PHP 构建语义清晰、可演进、高可用的接口体系。
一、API 设计的四个核心原则
无论 RESTful 还是 GraphQL,优秀 API 都遵循同一套底层逻辑:
| 原则 | 说明 |
|---|---|
| 资源导向 | API 描述"资源",而非"动作" |
| 契约优先 | 类型、结构、错误码先于实现 |
| 可演进性 | 向后兼容、版本控制 |
| 可观测性 | 日志、指标、追踪 |
✅ API 是产品,不是函数集合。
二、RESTful API:用 HTTP 做设计语言
1. 资源建模(最关键的一步)
REST 的核心是:一切皆资源。
/users
/users/{id}
/users/{id}/posts
/posts/{id}/comments
❌ 错误示例(RPC 思维):
/getUser
/createUser
/deleteUser
✅ 正确示例(资源思维):
GET /users/{id}
POST /users
DELETE /users/{id}
2. HTTP 方法语义
| 方法 | 语义 | 幂等 |
|---|---|---|
| GET | 查询 | ✅ |
| POST | 新建 | ❌ |
| PUT | 整体替换 | ✅ |
| PATCH | 局部更新 | ❌ |
| DELETE | 删除 | ✅ |
3. 状态码设计(被严重低估)
| 场景 | 状态码 |
|---|---|
| 成功查询 | 200 OK |
| 创建成功 | 201 Created |
| 异步接受 | 202 Accepted |
| 校验失败 | 400 Bad Request |
| 未认证 | 401 Unauthorized |
| 无权限 | 403 Forbidden |
| 资源不存在 | 404 Not Found |
| 版本冲突 | 409 Conflict |
| 限流 | 429 Too Many Requests |
| 服务错误 | 500 Internal Server Error |
⚠️ 不要所有错误都返回 200 + error 字段,那是反模式。
4. 请求 / 响应结构设计
4.1 统一响应格式
{
"data": { ... },
"meta": {
"total": 100,
"page": 1
},
"links": {
"self": "..."
}
}
4.2 错误结构
{
"error": {
"code": "USER_NOT_FOUND",
"message": "用户不存在",
"details": {}
}
}
5. 版本控制策略
✅ 推荐:URL 版本
/api/v1/users
✅ 兼容:Header 版本
Accept: application/vnd.app.v1+json
❌ 不推荐:参数版本
/users?version=1
6. 分页、过滤、排序
GET /users?page=2&per_page=20
GET /users?sort=-created_at,name
GET /users?filter[status]=active
✅ 使用标准字段,避免自定义方言。
三、GraphQL:精准、强类型、自描述
1. GraphQL 解决了什么问题?
| 问题 | REST | GraphQL |
|---|---|---|
| 过度获取 | ❌ | ✅ |
| 请求次数 | 多 | 少 |
| 类型安全 | 弱 | 强 |
| 自文档 | 需 Swagger | 内置 |
| 前端控制 | 弱 | 强 |
2. Schema 是 API 的核心
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Query {
user(id: ID!): User
users(page: Int): [User!]!
}
✅ Schema 即契约,优先于代码。
3. PHP 中的 GraphQL 实现
主流选择:
| 框架 | 方案 |
|---|---|
| Laravel | Lighthouse |
| Symfony | OverblogGraphQLBundle |
| 通用 | GraphQL-PHP |
4. Resolver 设计原则
class UserResolver
{
public function resolveUser(int $id): User
{
return UserRepository::findOrFail($id);
}
}
✅ **Resolver 只负责"取数据"**
✅ 业务逻辑仍在 Service / Domain 层
5. N+1 问题(GraphQL 最大陷阱)
query {
users {
posts {
comments {
author
}
}
}
}
✅ 解决方案:DataLoader(批处理 + 缓存)
class PostDataLoader
{
public function load(int $userId): array
{
// batch load
}
}
⚠️ 没有 DataLoader 的 GraphQL 等于性能自杀。
四、RESTful vs GraphQL:如何选择?
| 维度 | RESTful | GraphQL |
|---|---|---|
| 简单 CRUD | ✅ | ⚠️ |
| 复杂聚合 | ❌ | ✅ |
| 多端适配 | ❌ | ✅ |
| 缓存 | ✅ | ❌ |
| 学习成本 | 低 | 高 |
| 生态成熟度 | 高 | 中 |
✅ 推荐策略:
- 对外公共 API → REST
- 内部 BFF / 前端驱动 → GraphQL
- 混合架构 → Gateway 层统一出口
五、高可用 API 的 7 个工程实践
1. 限流(Rate Limiting)
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->ip());
});
2. 缓存策略
| 层级 | 技术 |
|---|---|
| HTTP | Cache-Control / ETag |
| 应用 | Redis |
| 数据 | Query Cache |
3. 幂等性设计
Idempotency-Key: uuid
✅ 对支付、下单等写操作至关重要。
4. 异步化
POST /exports
202 Accepted
Location: /tasks/123
5. 日志与追踪
- Request ID
- 结构化日志(JSON)
- OpenTelemetry / Jaeger
6. 安全
| 项目 | 实践 |
|---|---|
| 认证 | OAuth2 / JWT |
| 授权 | Policy / Gate |
| 输入 | 严格校验 |
| 输出 | 数据脱敏 |
7. 健康检查
GET /health
GET /health/ready
GET /health/live
六、API 设计反模式清单
❌ RPC 伪装 REST
❌ 一个接口解决所有问题
❌ 错误码全部 200
❌ GraphQL 无 DataLoader
❌ API 无版本管理
❌ 返回数据库原始结构
七、未来趋势:API 的下一站
| 方向 | 说明 |
|---|---|
| OpenAPI 3 | REST 的"类型系统" |
| Federation | GraphQL 微服务 |
| gRPC | 内部高性能 RPC |
| tRPC | 类型安全端到端 |
| Event-Driven API | Webhook / SSE |
✅ PHP 正在从"页面语言"变成"API 语言"。
八、结语:API 是长期主义的产物
好的 API 不是设计出来的,而是演化出来的。
- RESTful 教会我们 资源与语义
- GraphQL 教会我们 精准与组合
- 高可用教会我们 工程与约束
真正优秀的 API:
- 前端用得爽
- 后端改得稳
- 运维看得清
- 业务活得久