Skip to content

API Design Principles

API design is grounded in clarity, consistency, and usability. The following principles guide our decisions:

Table of Contents

  1. RESTful Design
  2. Resource-Oriented
  3. Statelessness
  4. Consistency
  5. Idempotency
  6. Versioning
  7. Error Handling
  8. Performance
  9. Security
  10. Documentation
  11. Evolution
  12. Best Practices

RESTful Design

1. Client-Server Architecture

  • Separation of Concerns: Clear separation between client and server
  • Stateless Communication: Each request contains all necessary information
  • Cacheable: Responses define cacheability
  • Uniform Interface: Standardized way of interacting with resources
  • Layered System: Client can't tell if connected to end server or intermediary

2. Resource Identification

  • Use nouns (not verbs) to identify resources
  • Use plural nouns for collections (/users not /user)
  • Use forward slashes (/) for hierarchy (/users/123/orders)
  • Use hyphens (-) for multi-word path segments (/product-categories)
  • Use lowercase letters in paths and headers
  • Never use file extensions in URIs

Resource-Oriented

1. Resource Naming

  • Pluralization: Use plural for collections (/users)
  • Consistency: Stick to either plural or singular (prefer plural)
  • Concreteness: Use concrete rather than abstract names
  • Avoid Verbs: Use HTTP methods to indicate actions

2. Resource Relationships

  • Hierarchy: Parent/child relationships via path segments (/users/123/orders)
  • References: Use IDs to reference related resources
  • Embedding: Include related resources when it improves efficiency
  • Pagination: Always paginate collections

Statelessness

1. Stateless Servers

  • No client context stored on server between requests
  • Each request contains all necessary authentication/authorization
  • Session state held entirely on the client
  • Improves reliability and scalability

2. Authentication & Authorization

  • Use stateless authentication (JWT, OAuth2)
  • Include auth tokens in headers
  • Implement proper token expiration and refresh
  • Follow principle of least privilege

Consistency

1. Naming Conventions

  • camelCase for JSON properties
  • kebab-case for URLs and headers
  • UPPER_SNAKE_CASE for constants and enums
  • PascalCase for type names in documentation

2. Response Format

{
  "data": {
    "id": "123",
    "type": "users",
    "attributes": {
      "name": "John Doe",
      "email": "john@example.com"
    },
    "relationships": {
      "organization": {
        "data": { "id": "456", "type": "organizations" }
      }
    }
  },
  "included": [
    {
      "id": "456",
      "type": "organizations",
      "attributes": {
        "name": "Acme Inc."
      }
    }
  ],
  "meta": {
    "total": 100,
    "page": 1,
    "limit": 10
  },
  "links": {
    "self": "/users?page=1",
    "next": "/users?page=2",
    "prev": null,
    "first": "/users?page=1",
    "last": "/users?page=10"
  }
}

Idempotency

1. Safe Methods

  • GET, HEAD, OPTIONS, and TRACE are naturally idempotent
  • Multiple identical requests have the same effect as a single request

2. Making Mutations Idempotent

  • PUT: Replace entire resource (idempotent)
  • PATCH: Partial updates (make idempotent with versioning)
  • DELETE: Idempotent by nature
  • POST: Not idempotent (use idempotency keys)

3. Idempotency Keys

POST /payments
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Content-Type: application/json

{
  "amount": 1000,
  "currency": "USD",
  "description": "Subscription payment"
}

Versioning

1. Versioning Strategies

  • URI Path: /v1/users
  • Header: Accept: application/vnd.dexaminds.v1+json
  • Query Param: /users?version=1 (not recommended)
  • Media Type: application/vnd.dexaminds+json;version=1

2. Versioning Best Practices

  • Version early, version often
  • Support multiple versions in parallel
  • Provide clear deprecation policies
  • Document version changes thoroughly

Error Handling

1. Error Response Format

{
  "error": {
    "code": "validation_error",
    "message": "One or more validation errors occurred",
    "target": "user_creation",
    "details": [
      {
        "code": "required",
        "target": "email",
        "message": "Email is required"
      },
      {
        "code": "min_length",
        "target": "password",
        "message": "Password must be at least 8 characters"
      }
    ],
    "innerError": {
      "code": "validation_failed",
      "requestId": "a1b2c3d4",
      "timestamp": "2025-06-30T06:27:45Z"
    }
  }
}

2. HTTP Status Codes

  • 2xx: Success
  • 4xx: Client errors
  • 5xx: Server errors
  • Use specific status codes (e.g., 429 for rate limiting)

Performance

1. Caching

  • Client Caching: Cache-Control, ETag, Last-Modified
  • Server Caching: Redis, Memcached
  • CDN Caching: For static assets

2. Pagination

  • Offset-based: ?page=2&limit=25
  • Cursor-based: ?after=cursor&limit=25
  • Keyset Pagination: ?since_id=123&limit=25

3. Filtering & Sorting

  • Filtering: ?status=active&role=admin
  • Sorting: ?sort=-created_at,name
  • Field Selection: ?fields=id,name,email

Security

1. Authentication

  • OAuth 2.0 with OpenID Connect
  • JWT for stateless authentication
  • API keys for service-to-service
  • MFA for sensitive operations

2. Authorization

  • Role-Based Access Control (RBAC)
  • Attribute-Based Access Control (ABAC)
  • Fine-grained permissions
  • Policy-based authorization

3. Input Validation

  • Validate all input data
  • Use allow-listing
  • Sanitize output
  • Prevent injection attacks

Documentation

1. API Reference

  • Endpoint descriptions
  • Request/response examples
  • Error codes
  • Authentication requirements

2. Interactive Documentation

  • Swagger/OpenAPI
  • Postman collections
  • API explorers

3. Guides & Tutorials

  • Getting started
  • Authentication
  • Common use cases
  • Migration guides

Evolution

1. Backward Compatibility

  • Add new fields as optional
  • Don't remove or rename fields
  • Use feature flags
  • Version your API

2. Deprecation Policy

  • Announce deprecations in advance
  • Provide migration guides
  • Maintain deprecated versions for a period
  • Monitor usage of deprecated endpoints

Best Practices

1. Design First

  • Design before implementation
  • Use API-first approach
  • Get feedback early
  • Iterate on design

2. Keep It Simple (KISS)

  • Favor simplicity over complexity
  • Follow the principle of least surprise
  • Avoid over-engineering
  • Make common tasks easy

3. Don't Repeat Yourself (DRY)

  • Reuse schemas and components
  • Centralize common functionality
  • Avoid code duplication
  • Use references in OpenAPI/Swagger

4. You Aren't Gonna Need It (YAGNI)

  • Implement features when needed
  • Avoid speculative generality
  • Keep the API surface small
  • Focus on current requirements

5. Principle of Least Privilege

  • Grant minimum necessary permissions
  • Use scopes for OAuth
  • Implement proper access controls
  • Audit permissions regularly

6. Fail Fast, Fail Loudly

  • Validate early
  • Provide clear error messages
  • Don't hide errors
  • Log failures appropriately

Anti-Patterns to Avoid

1. Over-fetching/Under-fetching

  • Problem: Getting too much or too little data
  • Solution: Use field selection and sparse fieldsets

2. Chatty APIs

  • Problem: Too many round-trips
  • Solution: Batch requests or use GraphQL

3. Ignoring Caching

  • Problem: Unnecessary load on servers
  • Solution: Implement proper caching headers

4. Ignoring Rate Limiting

  • Problem: API abuse and DDoS attacks
  • Solution: Implement rate limiting and quotas

5. Poor Error Handling

  • Problem: Unhelpful error messages
  • Solution: Standardized error responses with codes

API Maturity Model

Level 0: HTTP as a Tunnel

  • Single POST endpoint
  • All actions as parameters
  • No REST principles

Level 1: Resources

  • Multiple URIs
  • Resources as nouns
  • Still mostly RPC-style

Level 2: HTTP Verbs

  • Proper use of HTTP methods
  • Status codes
  • Still some RPC endpoints

Level 3: Hypermedia Controls

  • HATEOAS
  • Discoverable APIs
  • Self-documenting

Measuring API Quality

1. Performance Metrics

  • Response time (p50, p95, p99)
  • Throughput (requests/second)
  • Error rate
  • Uptime/availability

2. Developer Experience

  • Time to first successful call
  • Documentation quality
  • SDK availability
  • Community support

3. Business Metrics

  • API adoption rate
  • Active developers
  • Revenue impact
  • Cost per API call

Conclusion

Designing great APIs requires careful consideration of many factors. By following these principles, you can create APIs that are:

  • Intuitive: Easy to understand and use
  • Efficient: Fast and resource-friendly
  • Reliable: Consistent and available
  • Secure: Protected against threats
  • Maintainable: Easy to evolve over time

Remember that API design is an iterative process. Continuously gather feedback from your consumers and be prepared to make improvements based on real-world usage patterns.