Design or review an HTTP/REST/GraphQL API for versioning, pagination, error shapes, idempotency, auth, and evolvability. Use when asked to "design an API", "sha
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-api-design-88663b5cdc09 ,按照其中的说明把「api-design」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
Invocation points:
<core_principle> CALLERS OUTLIVE YOUR ASSUMPTIONS. An API you ship today has to keep working when your internals change, when the mobile app version two is still in use, and when a third party integrates against it. Design for extension, not just for the current caller.
HONEST STATUS CODES. 200 OK with {"error": "not found"} is a lie. 404 says not found. Use the HTTP semantics the protocol offers — HTTP clients, caches, and intermediaries rely on them.
PAGINATION IS NON-OPTIONAL. Any list endpoint that doesn't paginate will eventually get a request for "all records" that kills your database. </core_principle>
Answer, or ask (one round, 1–3 questions):
/v1/) / header-based / GraphQL schema evolution.POST /users, not POST /createUser./users/42, not /user/42./users/42/sessions/3. Otherwise flat: /sessions/3?userId=42.POST /users/42:deactivate (colon syntax) or POST /users/42/actions/deactivate.user(id), createUser(input), deactivateUser(id).createUser(input: CreateUserInput!).| Method | Intent | Idempotent? | Default success |
|---|---|---|---|
| GET | Read | Yes | 200, or 304 if conditional |
| POST | Create or non-idempotent action | No | 201 with Location on create, 200 on action |
| PUT | Replace (full-object) | Yes | 200 with body, or 204 |
| PATCH | Partial update | No (usually) | 200 with body |
| DELETE | Remove | Yes | 204 |
Errors:
Retry-AfterRetry-AfterNever 200-with-error-body. Never 500 for a 4xx cause.
errors[]) for transport-level failures. Domain errors (validation, not-found, forbidden) go in the typed return — use a union or result type.null on a field is meaningful, not a signal of generic failure.limit, return nextCursor when more exists. Scales, stable under writes.limit (e.g., 200).edges, pageInfo) if the ecosystem expects it; otherwise a simpler {items, nextCursor} is fine.Standardize one shape and use it everywhere. Example REST:
{
"error": {
"code": "user_not_found",
"message": "No user with id 42",
"details": { "userId": 42 },
"requestId": "req_abc123"
}
}
code is machine-readable; stable; documented.message is human-readable; can change.details carries structured context.requestId lets callers report bugs.Errors don't leak stack traces, file paths, or internal queries.
Idempotency-Key: <uuid>; server dedupes for a window.If-Match on PUT/PATCH)./v2/), sunset headers on /v1/, deprecation window communicated. Or, for GraphQL, @deprecated on fields with a migration note.If this is a review, produce findings in the same shape as security-review / review — file:line, category, recommendation.
If this is a new design, produce:
## <API name>
### Scope
<what the API is for, who calls it>
### Endpoints / Operations
- `POST /users` — create user. Request: `{email, name}`. Response 201: `{id, email, name, createdAt}` + `Location: /users/<id>`. Errors: 409 email taken, 422 invalid.
- ...
### Auth
<model + where to put the credential>
### Pagination
<cursor shape, max limit>
### Error shape
<one canonical shape>
### Idempotency / concurrency
<rules>
### Versioning
<stance + how breaking changes will be handled>
### OpenAPI / SDL
<link or inline>
Append architectural decisions to .gsd/DECISIONS.md.
<anti_patterns>
{"error": "..."}. Lies to caches, proxies, retry libraries.GET /users without a limit cap will bite you.</anti_patterns>
<success_criteria>
.gsd/DECISIONS.md.</success_criteria>