# API Designer

> REST and GraphQL API design expertise covering REST maturity model, resource naming, pagination patterns, filtering and sorting, HATEOAS, versioning strategies, error response formats, rate limiting headers, and OpenAPI specification. Use when the user asks about api designer, api designer best practices, or needs guidance on api designer implementation. Do NOT use when the user needs a different specialized skill or is asking about an unrelated technology domain.

- Skill: `ferroxlabs/api-designer` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add ferroxlabs/api-designer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ferroxlabs/api-designer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: Apache-2.0
- Author: FerroxLabs (https://skillmd.com/u/ferroxlabs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ferroxlabs/api-designer

---


# API Designer

## Purpose

Design consistent, intuitive, and evolvable HTTP APIs. This skill covers REST API design from resource modeling through error handling, pagination, versioning, and documentation with OpenAPI. GraphQL design considerations are included for comparison and hybrid architectures.

## REST Maturity Model (Richardson)

```
Level 0: The Swamp of POX
  - Single endpoint, single HTTP method (POST)
  - RPC-style: POST /api { "action": "getUser", "id": 123 }
  - Avoid this.

Level 1: Resources
  - Multiple endpoints, one per resource
  - POST /users, POST /users/123/delete
  - Still misusing HTTP methods.

Level 2: HTTP Verbs (TARGET for most APIs)
  - Resources + correct HTTP methods
  - GET /users, POST /users, PUT /users/123, DELETE /users/123
  - Correct status codes (200, 201, 204, 400, 404, etc.)

Level 3: HATEOAS (Hypermedia)
  - Level 2 + hypermedia controls in responses
  - Clients discover actions from response links
  - Full REST (rarely implemented in practice)
```

## Resource Naming Conventions

### Rules

```
1. Use NOUNS, not verbs
   GOOD: GET /users
   BAD:  GET /getUsers

2. Use PLURAL nouns
   GOOD: /users, /orders, /products
   BAD:  /user, /order, /product

3. Use kebab-case for multi-word resources
   GOOD: /order-items, /user-profiles
   BAD:  /orderItems, /user_profiles

4. Nest resources to show relationships (max 2 levels)
   GOOD: /users/123/orders
   BAD:  /users/123/orders/456/items/789/reviews  (too deep)

5. Use query parameters for filtering, not path segments
   GOOD: GET /orders?status=pending&userId=123
   BAD:  GET /orders/pending/user/123

6. Actions on resources (when necessary)
   POST /orders/123/cancel    (state transition)
   POST /users/123/verify     (trigger action)
```

### Standard HTTP Methods

```
GET     /resources          -> List resources (200)
GET     /resources/:id      -> Get single resource (200 or 404)
POST    /resources          -> Create resource (201 with Location header)
PUT     /resources/:id      -> Full replace (200 or 204)
PATCH   /resources/:id      -> Partial update (200 or 204)
DELETE  /resources/:id      -> Delete resource (204 or 200)

HEAD    /resources/:id      -> Same as GET but no body (check existence)
OPTIONS /resources          -> Available methods (CORS preflight)
```

### Standard Status Codes

```
2xx SUCCESS:
  200 OK                    -> GET, PUT, PATCH with response body
  201 Created               -> POST (include Location header)
  204 No Content            -> DELETE, PUT/PATCH with no response body

3xx REDIRECTION:
  301 Moved Permanently     -> Resource permanently at new URL
  304 Not Modified          -> Conditional GET (ETag match)

4xx CLIENT ERROR:
  400 Bad Request           -> Validation error, malformed request
  401 Unauthorized          -> Missing or invalid authentication
  403 Forbidden             -> Authenticated but not authorized
  404 Not Found             -> Resource does not exist
  405 Method Not Allowed    -> HTTP method not supported
  409 Conflict              -> Resource state conflict (duplicate, version)
  422 Unprocessable Entity  -> Valid syntax but semantic errors
  429 Too Many Requests     -> Rate limit exceeded

5xx SERVER ERROR:
  500 Internal Server Error -> Unexpected server failure
  502 Bad Gateway           -> Upstream service failure
  503 Service Unavailable   -> Temporary (include Retry-After header)
  504 Gateway Timeout       -> Upstream timeout
```

## Pagination Patterns

### Offset-Based Pagination

```
REQUEST:
  GET /users?page=2&per_page=20

RESPONSE:
  {
    "data": [...],
    "pagination": {
      "page": 2,
      "per_page": 20,
      "total_items": 157,
      "total_pages": 8,
      "has_next": true,
      "has_prev": true
    },
    "links": {
      "first": "/users?page=1&per_page=20",
      "prev":  "/users?page=1&per_page=20",
      "next":  "/users?page=3&per_page=20",
      "last":  "/users?page=8&per_page=20"
    }
  }

PROS:
  - Simple to implement
  - Allows jumping to arbitrary page
  - Total count available

CONS:
  - Inconsistent with inserts/deletes during pagination
  - Poor performance on large offsets (OFFSET N is O(N))
  - Items can be skipped or duplicated between pages
```

### Cursor-Based Pagination (Recommended for Large Datasets)

```
REQUEST:
  GET /users?limit=20&after=eyJ0IjoxNjk...

RESPONSE:
  {
    "data": [...],
    "pagination": {
      "has_next": true,
      "has_prev": true,
      "next_cursor": "eyJ0IjoxNjk...",
      "prev_cursor": "eyJ0IjoxNjg..."
    }
  }

PROS:
  - Consistent pagination (no skips/dupes)
  - Efficient for any position in dataset
  - Works well with real-time data

CONS:
  - Cannot jump to arbitrary page
  - No total count (requires separate query)
  - Cursor is opaque (not user-friendly)

CURSOR IMPLEMENTATION:
  Encode the last item's sort key(s) as base64:
  { "created_at": "2025-01-15T10:30:00Z", "id": "abc123" }
  -> base64 -> "eyJ0IjoxNjk..."

SQL:
  WHERE (created_at, id) < (:cursor_created_at, :cursor_id)
  ORDER BY created_at DESC, id DESC
  LIMIT :limit + 1  -- get one extra to check has_next
```

### Keyset Pagination

```
REQUEST:
  GET /users?limit=20&created_before=2025-01-15T10:30:00Z&id_before=abc123

RESPONSE:
  Same as cursor-based but with explicit keys instead of opaque cursor.

PROS: Transparent and debuggable
CONS: Exposes sort implementation
```

## Filtering and Sorting

### Filtering Patterns

```
Simple equality:
  GET /users?status=active&role=admin

Operators (LHS brackets):
  GET /orders?amount[gte]=100&amount[lte]=500
  GET /users?name[contains]=john
  GET /orders?created_at[after]=2025-01-01

Multiple values (comma-separated OR):
  GET /users?role=admin,editor

Search:
  GET /users?q=john+doe

Nested fields (dot notation):
  GET /users?address.city=Austin
```

### Sorting

```
Single sort:
  GET /users?sort=created_at
  GET /users?sort=-created_at     (descending with - prefix)

Multi-sort:
  GET /users?sort=-created_at,name  (primary: created_at desc, secondary: name asc)

Alternative format:
  GET /users?sort_by=created_at&sort_order=desc
```

### Field Selection (Sparse Fieldsets)

```
GET /users?fields=id,name,email        (only return these fields)
GET /users?include=orders,profile       (include related resources)
GET /users?exclude=password_hash        (exclude sensitive fields)
```

## Error Response Format

### Standard Error Envelope

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "The request body contains invalid data.",
    "details": [
      {
        "field": "email",
        "code": "INVALID_FORMAT",
        "message": "Must be a valid email address."
      },
      {
        "field": "age",
        "code": "OUT_OF_RANGE",
        "message": "Must be between 13 and 120.",
        "meta": { "min": 13, "max": 120 }
      }
    ],
    "request_id": "req_abc123xyz",
    "documentation_url": "[reference URL]"
  }
}
```

### Error Code Taxonomy

```
AUTHENTICATION:
  AUTH_REQUIRED          -> 401: No credentials provided
  AUTH_INVALID           -> 401: Invalid credentials
  AUTH_EXPIRED           -> 401: Token expired

AUTHORIZATION:
  FORBIDDEN              -> 403: Insufficient permissions
  RESOURCE_FORBIDDEN     -> 403: No access to this resource

VALIDATION:
  VALIDATION_ERROR       -> 400/422: Input validation failed
  INVALID_FORMAT         -> 400: Wrong data format
  MISSING_FIELD          -> 400: Required field missing

RESOURCE:
  NOT_FOUND              -> 404: Resource does not exist
  ALREADY_EXISTS         -> 409: Duplicate resource
  CONFLICT               -> 409: State conflict (version mismatch)
  GONE                   -> 410: Resource permanently deleted

RATE_LIMIT:
  RATE_LIMIT_EXCEEDED    -> 429: Too many requests

SERVER:
  INTERNAL_ERROR         -> 500: Unexpected error
  SERVICE_UNAVAILABLE    -> 503: Temporary outage
```

## Versioning Strategies

### Decision Matrix

```
STRATEGY              MECHANISM                   WHEN TO USE
-----------------------------------------------------------------------
URL Path              /v1/users, /v2/users        Simple, explicit, most common
Header                Accept: application/vnd.    More "RESTful", harder to test
                      api.v2+json
Query Param           /users?version=2            Easy migration, but ugly

RECOMMENDATION: URL path versioning (/v1/) for simplicity and clarity.
```

### Versioning Rules

```
1. Only bump major version for BREAKING changes:
   - Removing a field
   - Changing field type
   - Changing resource structure
   - Changing error format

2. NON-BREAKING changes (no version bump):
   - Adding new fields (with defaults)
   - Adding new endpoints
   - Adding new query parameters
   - Adding new enum values

3. Support at most 2 versions simultaneously
   v1 (deprecated, sunset date set) and v2 (current)

4. Include deprecation headers:
   Sunset: Sat, 01 Mar 2026 00:00:00 GMT
   Deprecation: true
   Link: <[reference URL]>; rel="successor-version"
```

## Rate Limiting Headers

```
Response headers for rate-limited endpoints:

X-RateLimit-Limit: 100          # Max requests per window
X-RateLimit-Remaining: 67       # Remaining requests in window
X-RateLimit-Reset: 1705312800   # Unix timestamp when window resets
Retry-After: 30                 # Seconds to wait (when 429 returned)

Alternative (IETF draft standard):
RateLimit-Policy: 100;w=3600    # 100 requests per 3600 seconds
RateLimit-Limit: 100
RateLimit-Remaining: 67
RateLimit-Reset: 42             # Seconds until reset (not timestamp)
```

## HATEOAS

```json
{
  "id": "ord_123",
  "status": "pending",
  "total": 99.99,
  "links": {
    "self":    { "href": "/orders/ord_123", "method": "GET" },
    "cancel":  { "href": "/orders/ord_123/cancel", "method": "POST" },
    "pay":     { "href": "/orders/ord_123/pay", "method": "POST" },
    "items":   { "href": "/orders/ord_123/items", "method": "GET" }
  }
}

// After payment, links change:
{
  "id": "ord_123",
  "status": "paid",
  "links": {
    "self":    { "href": "/orders/ord_123", "method": "GET" },
    "refund":  { "href": "/orders/ord_123/refund", "method": "POST" },
    "receipt": { "href": "/orders/ord_123/receipt", "method": "GET" }
    // "cancel" and "pay" no longer available
  }
}
```

## OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: Users API
  version: 1.0.0
  description: User management API
  contact:
    email: api@example.com

servers:
  - url: [reference URL]
    description: Production
  - url: [reference URL]
    description: Staging

paths:
  /users:
    get:
      operationId: listUsers
      summary: List all users
      tags: [Users]
      parameters:
        - name: page
          # ... (condensed) ...
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

security:
  - BearerAuth: []
```

## API Design Checklist

- [ ] Resources are nouns (plural), not verbs
- [ ] HTTP methods used correctly (GET reads, POST creates, etc.)
- [ ] Status codes are semantic and consistent
- [ ] Pagination implemented (cursor-based for large datasets)
- [ ] Filtering uses query parameters with consistent operators
- [ ] Sorting supports multiple fields with direction prefix
- [ ] Error responses follow standard envelope format
- [ ] Rate limiting headers included on all responses
- [ ] API versioned via URL path (/v1/)
- [ ] OpenAPI specification maintained and accurate
- [ ] Authentication returns 401, authorization returns 403
- [ ] Created resources return 201 with Location header
- [ ] Idempotency keys supported for POST operations
- [ ] Request IDs included in error responses for debugging
- [ ] Deprecation communicated via Sunset header

## When to Use

**Use this skill when:**
- Designing or implementing api designer solutions
- Reviewing or improving existing api designer approaches
- Making architectural or implementation decisions about api designer
- Learning api designer patterns and best practices
- Troubleshooting api designer-related issues

**Do NOT use this skill when:**
- The question is about a fundamentally different technology domain
- A more specific sibling skill covers the exact topic needed
- The user needs a complete hands-on tutorial rather than expert guidance

## Output Format

```markdown
# Api Designer Analysis

## Context Assessment
[Situation summary and constraints]

## Recommended Approach
[Primary recommendation with rationale]

## Implementation Steps
1. [Step with specific details]
2. [Step with specific details]
3. [Step with specific details]

## Trade-offs and Considerations
- [Key trade-off 1]
- [Key trade-off 2]

## Next Steps
- [Immediate action item]
- [Follow-up action item]
```

## Example

**Input:** "Help me implement api designer for a medium-scale production application"

**Output:** A structured analysis covering current state assessment, recommended api designer approach with specific patterns, implementation roadmap with milestones, and risk mitigation strategies tailored to the application scale and constraints.

## Edge Cases

- **Legacy system integration:** When api designer must coexist with legacy approaches, provide a gradual migration path rather than a complete rewrite
- **Scale mismatch:** When the solution complexity exceeds the project scale, recommend a simpler approach and note when to revisit
- **Team skill gaps:** When the team lacks experience with the recommended approach, include learning resources and simpler alternatives
- **Conflicting requirements:** When constraints conflict (e.g., performance vs. maintainability), explicitly state the trade-off and recommend based on stated priorities

