API Design Principles
WHAT
Design intuitive, scalable REST and GraphQL APIs that developers love. Covers resource modeling, HTTP semantics, pagination, error handling, versioning, and GraphQL schema patterns.
WHEN
- - Designing new REST or GraphQL APIs
- Reviewing API specifications before implementation
- Establishing API design standards for teams
- Refactoring APIs for better usability
- Migrating between API paradigms
KEYWORDS
REST, GraphQL, API design, HTTP methods, pagination, error handling, versioning, OpenAPI, HATEOAS, schema design
Decision Framework: REST vs GraphQL
| Choose REST when... | Choose GraphQL when... |
|---|
| Simple CRUD operations | Complex nested data requirements |
| Public APIs with broad audience |
Mobile apps needing bandwidth optimization |
| Heavy caching requirements | Clients need to specify exact data shape |
| Team is unfamiliar with GraphQL | Aggregating multiple data sources |
| Simple response structures | Rapidly evolving frontend requirements |
REST API Design
Resource Naming Rules
CODEBLOCK0
HTTP Methods and Status Codes
| Method | Purpose | Success | Common Errors |
|---|
| GET | Retrieve | 200 OK | 404 Not Found |
| POST |
Create | 201 Created | 400/422 Validation |
| PUT | Replace | 200 OK | 404 Not Found |
| PATCH | Partial update | 200 OK | 404 Not Found |
| DELETE | Remove | 204 No Content | 404/409 Conflict |
Complete Status Code Reference
CODEBLOCK1
Pagination
Offset-Based (Simple)
CODEBLOCK2
Cursor-Based (For Large Datasets)
CODEBLOCK3
Filtering and Sorting
CODEBLOCK4
Error Response Format
Always use consistent structure:
CODEBLOCK5
FastAPI Implementation
CODEBLOCK6
GraphQL API Design
Schema Structure
CODEBLOCK7
DataLoader (Prevent N+1)
CODEBLOCK8
Query Protection
CODEBLOCK9
Versioning Strategies
URL Versioning (Recommended)
CODEBLOCK10
Pros: Clear, easy to route, cacheable
Cons: Multiple URLs for same resource
Header Versioning
CODEBLOCK11
Pros: Clean URLs
Cons: Less visible, harder to test
Deprecation Strategy
- 1. Add deprecation headers: INLINECODE0
- Document migration path
- Give 6-12 months notice
- Monitor usage before removal
Rate Limiting
Headers
CODEBLOCK12
Implementation
CODEBLOCK13
Pre-Implementation Checklist
Resources
- - [ ] Nouns, not verbs
- [ ] Plural for collections
- [ ] Max 2 levels nesting
HTTP
- - [ ] Correct method for each action
- [ ] Correct status codes
- [ ] Idempotent operations are idempotent
Data
- - [ ] All collections paginated
- [ ] Filtering/sorting supported
- [ ] Error format consistent
Security
- - [ ] Authentication defined
- [ ] Rate limiting configured
- [ ] Input validation on all fields
- [ ] HTTPS enforced
Documentation
- - [ ] OpenAPI spec generated
- [ ] All endpoints documented
- [ ] Examples provided
NEVER
- - Verbs in URLs:
/api/getUser → use /api/users/{id} with GET - POST for Retrieval: Use GET for safe, idempotent reads
- Inconsistent Errors: Always same error format
- Unbounded Lists: Always paginate collections
- Secrets in URLs: Query params are logged
- Breaking Changes Without Versioning: Plan for evolution from day 1
- Database Schema as API: API should be stable even when schema changes
- Ignoring HTTP Semantics: Status codes and methods have meaning
API 设计原则
是什么
设计开发者喜爱的直观、可扩展的REST和GraphQL API。涵盖资源建模、HTTP语义、分页、错误处理、版本控制和GraphQL模式模式。
何时使用
- - 设计新的REST或GraphQL API时
- 在实现前审查API规范时
- 为团队建立API设计标准时
- 重构API以提高可用性时
- 在API范式之间迁移时
关键词
REST, GraphQL, API设计, HTTP方法, 分页, 错误处理, 版本控制, OpenAPI, HATEOAS, 模式设计
决策框架:REST vs GraphQL
| 选择REST的场景... | 选择GraphQL的场景... |
|---|
| 简单的CRUD操作 | 复杂的嵌套数据需求 |
| 面向广泛受众的公共API |
需要优化带宽的移动应用 |
| 大量缓存需求 | 客户端需要指定精确的数据形状 |
| 团队不熟悉GraphQL | 聚合多个数据源 |
| 简单的响应结构 | 快速演进的前端需求 |
REST API 设计
资源命名规则
✓ 集合使用复数名词
GET /api/users
GET /api/orders
GET /api/products
✗ 避免使用动词(让HTTP方法作为动词)
POST /api/createUser ← 错误
POST /api/users ← 正确
✓ 嵌套资源(最多2层)
GET /api/users/{id}/orders
✗ 避免深层嵌套
GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews ← 太深
GET /api/order-items/{id}/reviews ← 更好
HTTP方法和状态码
| 方法 | 用途 | 成功 | 常见错误 |
|---|
| GET | 检索 | 200 OK | 404 未找到 |
| POST |
创建 | 201 已创建 | 400/422 验证失败 |
| PUT | 替换 | 200 OK | 404 未找到 |
| PATCH | 部分更新 | 200 OK | 404 未找到 |
| DELETE | 删除 | 204 无内容 | 404/409 冲突 |
完整状态码参考
python
SUCCESS = {
200: OK, # GET, PUT, PATCH 成功
201: 已创建, # POST 成功
204: 无内容, # DELETE 成功
}
CLIENT_ERROR = {
400: 错误请求, # 语法错误
401: 未授权, # 缺少/无效认证
403: 禁止访问, # 有效认证,无权限
404: 未找到, # 资源不存在
409: 冲突, # 状态冲突(重复邮箱)
422: 不可处理的实体, # 验证错误
429: 请求过多, # 限流
}
SERVER_ERROR = {
500: 内部服务器错误,
503: 服务不可用, # 临时停机
}
分页
基于偏移量(简单)
python
GET /api/users?page=2&page_size=20
{
items: [...],
page: 2,
page_size: 20,
total: 150,
pages: 8
}
基于游标(适用于大数据集)
python
GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ
{
items: [...],
next_cursor: eyJpZCI6MTQzfQ,
has_more: true
}
过滤和排序
过滤
GET /api/users?status=active&role=admin
排序(-前缀表示降序)
GET /api/users?sort=-created_at,name
搜索
GET /api/users?search=john
字段选择
GET /api/users?fields=id,name,email
错误响应格式
始终使用一致的结构:
json
{
error: {
code: VALIDATION_ERROR,
message: 请求验证失败,
details: [
{field: email, message: 邮箱格式无效}
],
timestamp: 2025-10-16T12:00:00Z
}
}
FastAPI 实现
python
from fastapi import FastAPI, Query, Path, HTTPException, status
from pydantic import BaseModel, Field, EmailStr
from typing import Optional, List
from datetime import datetime
app = FastAPI(title=API, version=1.0.0)
模型
class UserCreate(BaseModel):
email: EmailStr
name: str = Field(..., min
length=1, maxlength=100)
class User(BaseModel):
id: str
email: str
name: str
created_at: datetime
class PaginatedResponse(BaseModel):
items: List[User]
total: int
page: int
page_size: int
pages: int
端点
@app.get(/api/users, response_model=PaginatedResponse)
async def list_users(
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
status: Optional[str] = Query(None),
search: Optional[str] = Query(None)
):
列出用户,支持分页和过滤。
total = await count_users(status=status, search=search)
offset = (page - 1) * page_size
users = await fetch
users(limit=pagesize, offset=offset, status=status, search=search)
return PaginatedResponse(
items=users,
total=total,
page=page,
pagesize=pagesize,
pages=(total + pagesize - 1) // pagesize
)
@app.post(/api/users, responsemodel=User, statuscode=status.HTTP201CREATED)
async def create_user(user: UserCreate):
创建新用户。
if await user_exists(user.email):
raise HTTPException(
statuscode=status.HTTP409_CONFLICT,
detail={code: EMAIL_EXISTS, message: 邮箱已注册}
)
return await save_user(user)
@app.get(/api/users/{userid}, responsemodel=User)
async def getuser(userid: str = Path(...)):
根据ID获取用户。
user = await fetchuser(userid)
if not user:
raise HTTPException(status_code=404, detail=用户未找到)
return user
@app.delete(/api/users/{userid}, statuscode=status.HTTP204NO_CONTENT)
async def deleteuser(userid: str):
删除用户。
if not await fetchuser(userid):
raise HTTPException(status_code=404, detail=用户未找到)
await removeuser(userid)
GraphQL API 设计
模式结构
graphql
类型
type User {
id: ID!
email: String!
name: String!
createdAt: DateTime!
orders(first: Int = 20, after: String): OrderConnection!
}
分页(Relay风格)
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
查询
type Query {
user(id: ID!): User
users(first: Int = 20, after: String, search: String): UserConnection!
}
变更(使用输入/负载模式)
input CreateUserInput {
email: String!
name: String!
password: String!
}
type CreateUserPayload {
user: User
errors: [Error!]
}
type Error {
field: String
message: String!
code: String!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
}
DataLoader(防止N+1问题)
python
from aiodataloader import DataLoader
class UserLoader(DataLoader):
async def batchloadfn(self, user_ids: List[str]) -> List[Optional[dict]]:
在单个查询中加载多个用户。
users = await fetchusersbyids(userids)
user_map = {user[id]: user for user in users}
return [usermap.get(uid) for uid in userids]
在解析器中
@user_type.field(orders)
async def resolve_orders(user: dict, info):
loader = info.context[loaders][orders
byuser]
return await loader.load(user[id])
查询保护
python
深度