Awesome-omni-skill api-design

RESTful and GraphQL API design principles, patterns, and best practices

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/api-design-jsmithdenverdev" ~/.claude/skills/diegosouzapw-awesome-omni-skill-api-design-efd922 && rm -rf "$T"
manifest: skills/development/api-design-jsmithdenverdev/SKILL.md
source content

What I do

I provide expertise in designing robust, scalable, and maintainable APIs:

  • RESTful API Design: Resource modeling, HTTP methods, status codes
  • GraphQL Schema Design: Types, queries, mutations, subscriptions
  • API Versioning: Strategies for evolving APIs without breaking clients
  • Error Handling: Consistent error responses and problem details
  • Authentication & Authorization: OAuth2, JWT, API keys, RBAC
  • Rate Limiting & Pagination: Scalability and performance patterns
  • Documentation: OpenAPI/Swagger, GraphQL introspection

When to use me

Load this skill when you need to:

  • Design a new API from scratch
  • Refactor or version an existing API
  • Implement consistent error handling
  • Add authentication/authorization
  • Optimize API performance
  • Document API contracts

REST API Principles

1. Resource-Oriented Design

GET    /users           # List users
POST   /users           # Create user
GET    /users/:id       # Get user
PUT    /users/:id       # Update user (full)
PATCH  /users/:id       # Update user (partial)
DELETE /users/:id       # Delete user

# Nested resources
GET    /users/:id/posts       # List user's posts
POST   /users/:id/posts       # Create post for user

2. HTTP Status Codes

2xx Success
  200 OK              - Successful GET, PUT, PATCH, DELETE
  201 Created         - Successful POST
  204 No Content      - Successful DELETE with no response body

4xx Client Errors
  400 Bad Request     - Invalid input
  401 Unauthorized    - Missing or invalid authentication
  403 Forbidden       - Authenticated but not authorized
  404 Not Found       - Resource doesn't exist
  409 Conflict        - Conflict with current state
  422 Unprocessable   - Validation errors

5xx Server Errors
  500 Internal Server Error
  503 Service Unavailable

3. Consistent Error Response Format

interface ProblemDetails {
  type: string;        // URI reference to problem type
  title: string;       // Short, human-readable summary
  status: number;      // HTTP status code
  detail?: string;     // Human-readable explanation
  instance?: string;   // URI reference to specific occurrence
  errors?: Record<string, string[]>; // Validation errors
}

// Example
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "The request contains invalid fields",
  "instance": "/users",
  "errors": {
    "email": ["Email is required", "Email must be valid"],
    "age": ["Age must be at least 18"]
  }
}

4. Pagination

// Cursor-based (recommended for large datasets)
GET /users?cursor=eyJpZCI6MTIzfQ&limit=20

{
  "data": [...],
  "pagination": {
    "nextCursor": "eyJpZCI6MTQzfQ",
    "hasMore": true
  }
}

// Offset-based (simpler, but less performant)
GET /users?page=2&limit=20

{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8
  }
}

5. Filtering, Sorting, Field Selection

# Filtering
GET /users?status=active&role=admin

# Sorting
GET /users?sort=createdAt:desc,name:asc

# Field selection (sparse fieldsets)
GET /users?fields=id,name,email

# Combined
GET /users?status=active&sort=name:asc&fields=id,name&limit=50

GraphQL Schema Design

1. Type System

type User {
  id: ID!
  email: String!
  name: String!
  posts: [Post!]!
  createdAt: DateTime!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
  published: Boolean!
  createdAt: DateTime!
  updatedAt: DateTime!
}

type Query {
  user(id: ID!): User
  users(filter: UserFilter, page: PageInput): UserConnection!
  post(id: ID!): Post
  posts(filter: PostFilter, page: PageInput): PostConnection!
}

type Mutation {
  createUser(input: CreateUserInput!): User!
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): Boolean!
}

input CreateUserInput {
  email: String!
  name: String!
  password: String!
}

input UserFilter {
  status: UserStatus
  role: UserRole
  search: String
}

2. Connection Pattern (Relay-style pagination)

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

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

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

3. Error Handling

// Use union types for operation results
type CreateUserResult = User | ValidationError | EmailTakenError

type ValidationError {
  message: String!
  fields: [FieldError!]!
}

type FieldError {
  field: String!
  message: String!
}

API Versioning Strategies

1. URL Versioning (Explicit)

/v1/users
/v2/users

2. Header Versioning (Preferred)

GET /users
Accept: application/vnd.myapi.v2+json

3. Query Parameter

GET /users?version=2

4. GraphQL Schema Stitching

# Deprecate fields gradually
type User {
  id: ID!
  name: String! @deprecated(reason: "Use firstName and lastName instead")
  firstName: String!
  lastName: String!
}

Authentication & Authorization

1. JWT Bearer Tokens

// Request
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

// Middleware
interface JWTPayload {
  sub: string;      // User ID
  email: string;
  role: string;
  iat: number;      // Issued at
  exp: number;      // Expiration
}

const authenticateJWT = (token: string): JWTPayload => {
  return jwt.verify(token, process.env.JWT_SECRET);
};

2. API Keys

// Request
X-API-Key: sk_live_1234567890abcdef

// Rate limiting per key
interface APIKey {
  key: string;
  userId: string;
  rateLimit: number;
  expiresAt?: Date;
}

3. Role-Based Access Control

enum Permission {
  READ_USERS = 'users:read',
  WRITE_USERS = 'users:write',
  DELETE_USERS = 'users:delete',
}

const authorize = (required: Permission[]) => {
  return (req, res, next) => {
    const userPermissions = req.user.permissions;
    const hasPermission = required.every(p => 
      userPermissions.includes(p)
    );
    
    if (!hasPermission) {
      return res.status(403).json({
        type: 'forbidden',
        title: 'Forbidden',
        status: 403,
      });
    }
    
    next();
  };
};

// Usage
app.delete('/users/:id', 
  authenticateJWT,
  authorize([Permission.DELETE_USERS]),
  deleteUser
);

Rate Limiting

1. Token Bucket Algorithm

interface RateLimiter {
  limit: number;        // Max requests
  window: number;       // Time window in seconds
  remaining: number;
  resetAt: Date;
}

// Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1640995200

2. Response when rate limited

// HTTP 429 Too Many Requests
{
  "type": "rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit. Try again later.",
  "retryAfter": 3600
}

// Header
Retry-After: 3600

API Documentation

OpenAPI 3.0 Example

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0
paths:
  /users:
    get:
      summary: List users
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
components:
  schemas:
    User:
      type: object
      required:
        - id
        - email
      properties:
        id:
          type: string
        email:
          type: string
          format: email

Best Practices

  1. Use nouns for resources, not verbs:
    /users
    not
    /getUsers
  2. Use plural nouns:
    /users
    not
    /user
  3. Return proper status codes: Don't return 200 for errors
  4. Include request IDs: For tracing and debugging
  5. Version your API: Plan for evolution
  6. Validate input: Use schemas (JSON Schema, Zod, etc.)
  7. Log everything: Requests, responses, errors
  8. Implement CORS properly: For web clients
  9. Use HTTPS: Always, no exceptions
  10. Document everything: OpenAPI, examples, guides

References

  • RESTful Web APIs by Leonard Richardson
  • GraphQL Best Practices: graphql.org/learn/best-practices
  • RFC 7807: Problem Details for HTTP APIs
  • OpenAPI Specification: swagger.io/specification