Skip to content
Back to skills

Api Design

ASecurity

Use when designing REST or GraphQL APIs - covers OpenAPI spec, resource design, versioning, and documentation

  • 31 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 7, 2026
developmenttypescriptgonodedebuggingrefactoringapidatabasedocumentation

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add JeremyDev87/codingbuddy --skill api-design --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Api Design?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Api Design
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/jeremydev87-api-design/badge)](https://www.skillsdirectory.com/skills/jeremydev87-api-design)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: api-design
description: Use when designing REST or GraphQL APIs - covers OpenAPI spec, resource design, versioning, and documentation
---

# API Design

## Overview

Design APIs that are consistent, predictable, and well-documented. This skill covers REST and GraphQL API design patterns.

**Core principle:** APIs are contracts. Once published, breaking changes are expensive.

## When to Use

**Use this skill:**
- Designing new API endpoints
- Refactoring existing APIs
- Adding versioning strategy
- Creating OpenAPI/GraphQL specifications
- Standardizing error responses

**Not needed for:**
- Internal function signatures
- Database schema design (use data-engineer agent)

## REST API Design

### Resource Naming

```
✅ Good                          ❌ Bad
-----------------------------------------
GET  /users                      GET  /getUsers
GET  /users/{id}                 GET  /user?id=123
POST /users                      POST /createUser
PUT  /users/{id}                 POST /updateUser
DELETE /users/{id}               GET  /deleteUser/{id}
GET  /users/{id}/orders          GET  /getUserOrders
```

**Rules:**
- Use nouns, not verbs (HTTP method is the verb)
- Plural for collections: `/users` not `/user`
- Hierarchical for relationships: `/users/{id}/orders`
- Lowercase, hyphen-separated: `/user-profiles` not `/userProfiles`

### HTTP Methods

| Method | Purpose | Idempotent | Safe |
|--------|---------|------------|------|
| GET | Retrieve resource | Yes | Yes |
| POST | Create resource | No | No |
| PUT | Replace resource | Yes | No |
| PATCH | Partial update | Yes* | No |
| DELETE | Remove resource | Yes | No |

*PATCH is idempotent if using JSON Patch/Merge Patch format.

### Status Codes

```
2xx Success
-----------------------------------------
200 OK              - GET/PUT/PATCH success, body contains data
201 Created         - POST success, Location header has URL
204 No Content      - DELETE success, no body

4xx Client Error
-----------------------------------------
400 Bad Request     - Invalid input (validation failed)
401 Unauthorized    - Authentication required
403 Forbidden       - Authenticated but not allowed
404 Not Found       - Resource doesn't exist
409 Conflict        - State conflict (duplicate, version mismatch)
422 Unprocessable   - Valid syntax but semantic error

5xx Server Error
-----------------------------------------
500 Internal Error  - Unexpected server error
502 Bad Gateway     - Upstream service failed
503 Unavailable     - Temporary overload
```

### Standard Response Format

**Success Response:**
```json
{
  "data": {
    "id": "123",
    "name": "Example"
  },
  "meta": {
    "requestId": "abc-123",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}
```

**Collection Response:**
```json
{
  "data": [
    { "id": "1", "name": "First" },
    { "id": "2", "name": "Second" }
  ],
  "meta": {
    "total": 100,
    "page": 1,
    "pageSize": 20,
    "hasNext": true
  }
}
```

**Error Response:**
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": [
      { "field": "email", "message": "Invalid email format" }
    ]
  },
  "meta": {
    "requestId": "abc-123",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}
```

### Versioning Strategies

| Strategy | Example | Pros | Cons |
|----------|---------|------|------|
| URI Path | `/v1/users` | Clear, cacheable | URL changes |
| Header | `Accept: application/vnd.api+json;version=1` | Clean URLs | Hidden |
| Query Param | `/users?version=1` | Easy to test | Pollutes params |

**Recommended:** URI Path versioning for simplicity and clarity.

```
/v1/users           # Version 1
/v2/users           # Version 2 (breaking changes)
```

**When to increment version:**
- Removing fields
- Changing field types
- Renaming fields
- Changing behavior

**Non-breaking (no version bump):**
- Adding new optional fields
- Adding new endpoints
- Adding new optional parameters

### OpenAPI Specification

```yaml
openapi: 3.1.0
info:
  title: User API
  version: 1.0.0
  description: User management endpoints

paths:
  /users:
    get:
      summary: List users
      operationId: listUsers
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserListResponse'
    post:
      summary: Create user
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: Created
          headers:
            Location:
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
          format: email
        name:
          type: string
        createdAt:
          type: string
          format: date-time

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
            details:
              type: array
              items:
                type: object
                properties:
                  field:
                    type: string
                  message:
                    type: string
```

## GraphQL API Design

### Schema Design

```graphql
type Query {
  user(id: ID!): User
  users(filter: UserFilter, pagination: Pagination): UserConnection!
}

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload!
  updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
  deleteUser(id: ID!): DeleteUserPayload!
}

type User {
  id: ID!
  email: String!
  name: String
  orders(first: Int, after: String): OrderConnection!
  createdAt: DateTime!
}

# Relay-style connection for pagination
type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

type UserEdge {
  cursor: String!
  node: User!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

# Input types
input CreateUserInput {
  email: String!
  name: String
}

# Payload types with union for errors
type CreateUserPayload {
  user: User
  errors: [UserError!]
}

type UserError {
  field: String
  message: String!
  code: String!
}
```

### N+1 Query Prevention

```typescript
// ❌ Bad: N+1 queries
const resolvers = {
  User: {
    orders: async (user) => {
      // Called once per user - N queries
      return db.orders.findByUserId(user.id);
    }
  }
};

// ✅ Good: DataLoader batching
import DataLoader from 'dataloader';

const orderLoader = new DataLoader(async (userIds) => {
  // Single batched query
  const orders = await db.orders.findByUserIds(userIds);
  return userIds.map(id => orders.filter(o => o.userId === id));
});

const resolvers = {
  User: {
    orders: async (user) => orderLoader.load(user.id)
  }
};
```

### Query Complexity Limiting

```typescript
// Prevent expensive queries
const complexityConfig = {
  maximumComplexity: 1000,
  scalarCost: 1,
  objectCost: 10,
  listFactor: 10, // Multiply by list size
};

// Example: users(first: 100) { orders(first: 50) { ... } }
// Complexity: 10 + (100 * (10 + (50 * 10))) = 51,010 (rejected)
```

## Rate Limiting

### Headers

```http
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200

# When exceeded
HTTP/1.1 429 Too Many Requests
Retry-After: 60
```

### Strategies

| Strategy | Description | Use Case |
|----------|-------------|----------|
| Fixed Window | X requests per minute | Simple APIs |
| Sliding Window | Rolling window | More accurate |
| Token Bucket | Refill tokens over time | Burst-friendly |
| Leaky Bucket | Constant output rate | Smooth traffic |

## API Gateway Patterns

```
Client → API Gateway → Microservices
              ↓
      - Authentication
      - Rate Limiting
      - Request Routing
      - Response Caching
      - Request/Response Transform
      - Circuit Breaker
```

## Documentation Checklist

Before shipping an API:

- [ ] OpenAPI/GraphQL schema complete
- [ ] All error codes documented
- [ ] Authentication described
- [ ] Rate limits documented
- [ ] Example requests/responses
- [ ] Changelog for versions
- [ ] SDK/client libraries linked
- [ ] Postman/Insomnia collection

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Verbs in URLs | Use nouns: `/users` not `/getUsers` |
| Inconsistent naming | Pick one: camelCase or snake_case |
| Missing pagination | Always paginate lists |
| No error codes | Use machine-readable codes |
| Breaking changes | Version the API |
| Missing rate limits | Protect against abuse |
| N+1 in GraphQL | Use DataLoader |
| No request IDs | Add for debugging |

## Quick Reference

```
REST
────────────────────────────────────
GET    /resources          List all
GET    /resources/{id}     Get one
POST   /resources          Create
PUT    /resources/{id}     Replace
PATCH  /resources/{id}     Update
DELETE /resources/{id}     Remove

GraphQL
────────────────────────────────────
Query       Read operations
Mutation    Write operations
Subscription Real-time updates

Connections (Relay)
────────────────────────────────────
edges       List of edge objects
node        The actual object
cursor      Pagination cursor
pageInfo    hasNextPage, etc.
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…