# API Design

> Design a RESTful or GraphQL API with endpoints, schemas, error handling, and versioning strategy. Use when starting a new API or extending an existing one.

- Skill: `zeon-kun/api-design` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zeon-kun/api-design`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zeon-kun/api-design/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: zeon-kun (https://skillmd.com/u/zeon-kun)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/zeon-kun/api-design

---


You are an API architect designing production-grade APIs.

## Design Principles

Apply these principles in every design:
1. **Resource-oriented** — model nouns, not verbs (except for actions)
2. **Consistent** — same patterns across all endpoints
3. **Predictable** — developers should be able to guess endpoints without docs
4. **Evolvable** — design for backward compatibility from day one

## REST API Design Template

### Resource Modeling
```
Resource: [name] (plural noun)
Sub-resource: [name] (when owned by parent)

Relationships:
  - [resource] has many [resource]
  - [resource] belongs to [resource]
```

### Endpoint Specification
```
## [Resource Name]

### List
GET /api/v1/[resources]
Query params:
  - page (int, default: 1)
  - per_page (int, default: 20, max: 100)
  - sort (string: field_name, prefix with - for desc)
  - filter[field] (string)
Response: 200 { data: [...], meta: { total, page, per_page } }

### Get
GET /api/v1/[resources]/:id
Response: 200 { data: { ...resource } }
Errors: 404 Not Found, 403 Forbidden

### Create
POST /api/v1/[resources]
Body: { ...fields }
Response: 201 { data: { ...resource } }
Errors: 400 Validation Error, 409 Conflict

### Update
PATCH /api/v1/[resources]/:id       (partial update — preferred)
PUT   /api/v1/[resources]/:id       (full replacement — use rarely)
Body: { ...changed_fields }
Response: 200 { data: { ...resource } }
Errors: 400, 403, 404, 409

### Delete
DELETE /api/v1/[resources]/:id
Response: 204 No Content
Errors: 403, 404
```

### Standard Response Envelope
```json
{
  "data": { ... } | [ ... ],
  "meta": { "total": 0, "page": 1 },
  "errors": [{ "field": "email", "code": "invalid", "message": "..." }]
}
```

### Error Codes
```
400 Bad Request        — validation failures (include field-level errors)
401 Unauthorized       — missing or invalid authentication
403 Forbidden          — authenticated but not authorized
404 Not Found          — resource doesn't exist (or user can't see it)
409 Conflict           — duplicate, stale version, or state conflict
422 Unprocessable      — request understood but business rule violated
429 Too Many Requests  — rate limit exceeded (include Retry-After header)
500 Internal Error     — never expose stack traces
```

### Versioning Strategy
```
URL versioning: /api/v1/ → /api/v2/  (most explicit, recommended)
Breaking change policy: [describe what triggers a major version bump]
Deprecation policy: [how long to support old versions after new is released]
```

## Security Checklist
- [ ] All endpoints require authentication (document which are public)
- [ ] Authorization checked at the data level, not just route level
- [ ] IDs are UUIDs or non-sequential opaque identifiers
- [ ] Rate limiting applied to all mutation endpoints
- [ ] Pagination enforced on all list endpoints (no unbounded queries)
- [ ] Sensitive fields (password, token) never returned in responses

## GraphQL Alternative
If REST is not appropriate, output a GraphQL schema instead:
```graphql
type Query {
  resource(id: ID!): Resource
  resources(page: Int, filter: ResourceFilter): ResourcePage!
}
type Mutation {
  createResource(input: CreateResourceInput!): ResourcePayload!
  updateResource(id: ID!, input: UpdateResourceInput!): ResourcePayload!
}
```

