Skip to content
intermediatePhase 51 · High-Level Design Framework

API Design

Design clean, versioned APIs with proper endpoints and contracts.

45m
0 problems
Topic Progress0%

RESTful Design Principles

REST Maturity Model (Richardson Maturity Model)

Level 0 — The Swamp of POX:
Single endpoint, HTTP as transport, no semantics.

POST /api
Content-Type: application/json

{"action": "getUser", "userId": 123}

Level 1 — Resources:
Multiple endpoints representing resources, but still single HTTP method.

GET /users/123
GET /orders/456

Level 2 — HTTP Verbs:
Proper use of HTTP methods (GET, POST, PUT, PATCH, DELETE).

GET /users/123          → Read user
POST /users             → Create user
PUT /users/123          → Update user
DELETE /users/123        → Delete user

Level 3 — Hypermedia Controls (HATEOAS):
Responses include links to related resources.

{
  "id": 123,
  "name": "John",
  "links": [
    {"rel": "orders", "href": "/users/123/orders"},
    {"rel": "self", "href": "/users/123"}
  ]
}

Resource Naming Conventions

Nouns, not verbs:

GET /users/123/orders          ✓
GET /getUserOrders?userId=123  ✗

Plural nouns for collections:

/users        → collection
/users/123    → specific resource

Nested resources for relationships:

/users/123/orders              → orders for user 123
/users/123/orders/456          → specific order for user 123
/orders/456/items              → items in order 456

Avoid deep nesting (max 3 levels):

/users/123/orders/456/items/789/reviews  ✗
/orders/456/items?userId=123             ✓

HTTP Methods and Status Codes

Method Purpose Idempotent Success Code
GET Read resource Yes 200 OK
POST Create resource No 201 Created
PUT Full update Yes 200 OK
PATCH Partial update No 200 OK
DELETE Remove resource Yes 204 No Content

Common Status Codes:

Code Meaning When to Use
200 OK Successful read or update
201 Created Resource successfully created
204 No Content Successful delete, no body returned
400 Bad Request Invalid input, validation failure
401 Unauthorized Missing or invalid authentication
403 Forbidden Authenticated but not authorized
404 Not Found Resource does not exist
409 Conflict Duplicate resource or version conflict
429 Too Many Requests Rate limit exceeded
500 Internal Server Error Unexpected server failure

Request/Response Design

Pagination:

GET /orders?page=2&limit=50

Response:
{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 50,
    "total": 1250,
    "totalPages": 25,
    "hasNext": true,
    "hasPrevious": true
  }
}

Cursor-based pagination (for large datasets):

GET /orders?cursor=abc123&limit=50

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

Filtering and Sorting:

GET /products?category=electronics&minPrice=50&maxPrice=500&sort=-createdAt

Sort prefix:
- ascending: createdAt
- descending: -createdAt

Error Response Format:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input data",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "password",
        "message": "Must be at least 8 characters"
      }
    ]
  }
}

HTTP Headers

Request Headers:

Authorization: Bearer <token>
Content-Type: application/json
Accept: application/json
X-Request-ID: uuid-v4
X-Idempotency-Key: uuid-v4

Response Headers:

Content-Type: application/json
X-Request-ID: uuid-v4
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 750
X-RateLimit-Reset: 1625097600
Cache-Control: max-age=3600
ETag: "abc123"

API Contracts & Documentation

OpenAPI Specification (Swagger)

OpenAPI is the industry standard for REST API documentation. It provides a machine-readable format that generates documentation, client SDKs, and server stubs.

Minimal OpenAPI structure:

openapi: 3.0.3
info:
  title: URL Shortener API
  version: 1.0.0
  description: API for creating and managing short URLs

paths:
  /urls:
    post:
      summary: Create a short URL
      operationId: createUrl
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUrlRequest'
      responses:
        '201':
          description: URL created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UrlResponse'
        '400':
          description: Invalid URL provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'

components:
  schemas:
    CreateUrlRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          format: uri
          example: "https://www.example.com/very/long/path"
        customAlias:
          type: string
          pattern: '^[a-zA-Z0-9_-]{3,20}$'
        expiresAt:
          type: string
          format: date-time

    UrlResponse:
      type: object
      properties:
        shortUrl:
          type: string
          example: "https://sho.rt/abc123"
        originalUrl:
          type: string
        createdAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
          nullable: true

    ErrorResponse:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string

API Contract Best Practices

1. Define request/response schemas explicitly

  • Every endpoint must have documented request body and response body schemas
  • Include examples for every field
  • Mark required vs optional fields

2. Use consistent naming conventions

 camelCase for JSON fields: userId, createdAt
 kebab-case for URL paths: /user-profiles
 UPPER_SNAKE_CASE for constants: RATE_LIMIT_ERROR

3. Document all error scenarios

  • 4xx errors: client-side issues (invalid input, auth failures)
  • 5xx errors: server-side issues (database down, timeout)
  • Include error codes and human-readable messages

4. Version the contract

  • Use semantic versioning (v1.0.0)
  • Document breaking vs non-breaking changes
  • Provide migration guides for major versions

Code Generation from OpenAPI

Once the OpenAPI spec is defined, generate:

  • Client SDKs: TypeScript, Python, Java, Go clients
  • Server stubs: Express, Flask, Spring Boot skeletons
  • Documentation: Interactive Swagger UI
  • Tests: Request/response validation tests

Tools:

  • OpenAPI Generator: Multi-language code generation
  • Swagger UI: Interactive documentation
  • Redoc: Beautiful three-panel documentation
  • Postman: Import OpenAPI for testing

Request Validation

Validate inputs before processing:

POST /urls
Content-Type: application/json

{
  "url": "not-a-valid-url"  ← should fail validation
}

Response: 400 Bad Request
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request body",
    "details": [
      {
        "field": "url",
        "message": "Must be a valid URL"
      }
    ]
  }
}

Validation layers:

  • Client-side: Immediate user feedback
  • API Gateway: Basic schema validation
  • Service layer: Business rule validation
  • Database: Constraint validation (last line of defense)

Versioning & Evolution

Versioning Strategies

1. URL Path Versioning (Most Common)

GET /v1/users/123
GET /v2/users/123
  • Pros: Explicit, easy to route, cacheable
  • Cons: URL proliferation, violates REST purity
  • Use when: Public APIs, major version changes

2. Header Versioning

GET /users/123
Accept: application/vnd.myapi.v2+json
  • Pros: Clean URLs, content negotiation
  • Cons: Harder to test in browser, less discoverable
  • Use when: Internal APIs, minor version changes

3. Query Parameter Versioning

GET /users/123?version=2
  • Pros: Simple to implement
  • Cons: Cache-busting issues, inconsistent
  • Use when: Quick prototyping, rarely recommended

Versioning Decision Matrix

Strategy Cacheability Discoverability Complexity Recommendation
URL Path High High Low Public APIs
Header Medium Low Medium Internal APIs
Query Param Low Medium Low Avoid

API Evolution Without Versioning

Additive changes (non-breaking):

  • Adding new fields to response
  • Adding new endpoints
  • Adding new optional request parameters
  • Adding new enum values

Breaking changes (require versioning):

  • Removing or renaming fields
  • Changing field types
  • Changing endpoint URLs
  • Requiring new fields
  • Changing authentication mechanism

Rate Limiting and Throttling

Rate Limiting:
Limits the number of requests a client can make in a time window.

Rate Limit Headers:
X-RateLimit-Limit: 1000       # requests per window
X-RateLimit-Remaining: 750    # requests left
X-RateLimit-Reset: 1625097600 # window reset time (epoch)

When exceeded:
HTTP 429 Too Many Requests
Retry-After: 60              # seconds to wait

Throttling:
Slows down requests rather than rejecting them. Uses token bucket or leaky bucket algorithms.

Implementation patterns:

Token Bucket:

Bucket capacity: 100 tokens
Refill rate: 10 tokens/second
Each request consumes 1 token
If bucket empty → reject request

Sliding Window:

Window: 1 minute
Max requests: 100
Track timestamps of last 100 requests
If new request and window full → reject

Backoff strategies:

Exponential Backoff:

Attempt 1: wait 1s
Attempt 2: wait 2s
Attempt 3: wait 4s
Attempt 4: wait 8s
Max: wait 60s

Jitter + Exponential Backoff:

wait = min(base * 2^attempt + random(0, 1000), maxWait)
  • Prevents thundering herd when many clients retry simultaneously

GraphQL vs REST

Factor REST GraphQL
Endpoint design Multiple endpoints Single endpoint
Data fetching Over/under-fetching common Exact data requested
Caching HTTP caching built-in Requires custom caching
Versioning Explicit versioning Schema evolution
Learning curve Lower Higher
Tooling Mature Growing
Use case Simple CRUD, public APIs Complex data requirements, mobile apps

gRPC vs REST

Factor REST gRPC
Protocol HTTP/1.1 or HTTP/2 HTTP/2
Data format JSON Protocol Buffers (binary)
Performance Text-based, larger payloads Binary, smaller payloads
Streaming Limited Full bidirectional streaming
Browser support Native Requires gRPC-Web
Code generation Manual Automatic from .proto files
Use case Public APIs, web apps Microservices, real-time systems

URL Shortener API Design Example

Endpoints:

POST   /v1/urls              → Create short URL
GET    /v1/urls/{shortCode}  → Redirect to original
GET    /v1/urls/{shortCode}/stats → Get click analytics
DELETE /v1/urls/{shortCode}  → Delete short URL
GET    /v1/users/{userId}/urls → List user's URLs

Create URL Request:

POST /v1/urls
{
  "url": "https://www.example.com/very/long/path?with=params",
  "customAlias": "my-link",
  "expiresAt": "2026-12-31T23:59:59Z"
}

Create URL Response:

201 Created
{
  "shortUrl": "https://sho.rt/my-link",
  "shortCode": "my-link",
  "originalUrl": "https://www.example.com/very/long/path?with=params",
  "createdAt": "2026-08-15T10:30:00Z",
  "expiresAt": "2026-12-31T23:59:59Z",
  "clickCount": 0
}

Redirect Response:

GET /v1/urls/abc123
302 Found
Location: https://www.example.com/very/long/path?with=params

Analytics Response:

200 OK
{
  "shortCode": "abc123",
  "totalClicks": 1542,
  "clicksByDate": [
    {"date": "2026-08-15", "clicks": 342},
    {"date": "2026-08-14", "clicks": 298}
  ],
  "topReferrers": [
    {"referrer": "twitter.com", "clicks": 456},
    {"referrer": "reddit.com", "clicks": 312}
  ]
}

API Security Checklist

  • Authentication: JWT tokens, API keys, OAuth 2.0
  • Authorization: Role-based access control (RBAC)
  • Rate limiting: Per-user and per-endpoint limits
  • Input validation: Sanitize all inputs
  • HTTPS: TLS encryption for all traffic
  • CORS: Restrict allowed origins
  • Idempotency keys: Prevent duplicate operations
  • Request signing: HMAC for webhook verification

Practice Problems

0/3solved
Design API Design (HLD) System

Design a scalable API Design (HLD) system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
API Design (HLD) Scaling

How would you scale API Design (HLD) to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
API Design (HLD) Failure Modes

Analyze potential failure modes for API Design (HLD) and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. Which Richardson Maturity Model level includes hypermedia controls (HATEOAS)?

Question 1 options

2. What is the correct HTTP status code for a successfully created resource?

Question 2 options

3. When should you prefer cursor-based pagination over offset-based pagination?

Question 3 options

4. What problem does an idempotency key solve?

Question 4 options

5. What is the main advantage of GraphQL over REST for mobile applications?

Question 5 options

Flashcards

Question

What are the 4 levels of the Richardson Maturity Model?

Answer

Level 0: Single endpoint, no resource semantics. Level 1: Multiple resources. Level 2: HTTP verbs (GET, POST, PUT, DELETE). Level 3: HATEOAS (hypermedia controls in responses).

Question

What is the difference between PUT and PATCH?

Answer

PUT replaces the entire resource with the provided data (full update). PATCH applies partial updates to specific fields. PUT is idempotent; PATCH may or may not be depending on implementation.

Question

When would you choose gRPC over REST?

Answer

gRPC is preferred for: microservice-to-microservice communication, real-time streaming, low-latency requirements, and when you need automatic code generation from .proto files. REST is better for public APIs and browser clients.

Question

What is the purpose of the Retry-After header?

Answer

The Retry-After header tells the client how many seconds to wait before retrying a request, typically included with 429 (Too Many Requests) or 503 (Service Unavailable) responses.

Question

Name 3 non-breaking API changes.

Answer

1) Adding new fields to response objects. 2) Adding new endpoints. 3) Adding new optional request parameters. These changes do not break existing clients.

Revision Notes

Key Takeaways

  • 1.Always use plural nouns for resource naming (/users, not /user)
  • 2.Return appropriate HTTP status codes — 201 for create, 204 for delete
  • 3.Implement pagination for all list endpoints to prevent unbounded responses
  • 4.Use idempotency keys for critical operations like payments and orders
  • 5.Version your API from day one using URL path versioning for public APIs
  • 6.Rate limit per user and per endpoint with clear retry-after instructions

Interview Tips

  • Start by defining the core resources (nouns) before designing endpoints
  • Always discuss pagination strategy — interviewers will ask about it
  • Mention rate limiting as a design consideration, not an afterthought
  • Compare REST vs GraphQL vs gRPC when asked about API choices
  • Show the full request/response cycle for at least one endpoint
  • Discuss error handling and validation as part of your API design

Cheat Sheet

API Design Cheat Sheet

REST Best Practices

  • Use nouns for resources: /users, /orders, /products
  • Use plural nouns: /users not /user
  • Use HTTP verbs correctly: GET(read), POST(create), PUT(update), DELETE(remove)
  • Max 3 levels of nesting: /users/123/orders/456

Status Code Quick Reference

  • 200: Success
  • 201: Created (include Location header)
  • 204: No Content (successful delete)
  • 400: Bad Request (invalid input)
  • 401: Unauthorized (no/invalid auth)
  • 403: Forbidden (no permission)
  • 404: Not Found
  • 429: Rate Limited
  • 500: Server Error

Pagination Comparison

Type Best For Cons
Offset Small datasets Slow for deep pages
Cursor Large datasets No random page access
Keyset Sorted data Requires indexed column

Rate Limiting Algorithms

  • Token Bucket: Flexible burst handling
  • Fixed Window: Simple, but burst at boundary
  • Sliding Window: Smooth, more accurate
  • Leaky Bucket: Constant output rate

Versioning Strategy Selection

Strategy Best For
URL Path (/v1/) Public APIs
Header Internal APIs
Query Param Avoid

GraphQL vs REST Decision

  • GraphQL: Mobile apps, complex data needs, multiple clients
  • REST: Simple CRUD, public APIs, HTTP caching important
  • gRPC: Microservices, streaming, binary performance