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
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 & reliabilityHow 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 decompositionAnalyze 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 degradationQuiz
1. Which Richardson Maturity Model level includes hypermedia controls (HATEOAS)?
2. What is the correct HTTP status code for a successfully created resource?
3. When should you prefer cursor-based pagination over offset-based pagination?
4. What problem does an idempotency key solve?
5. What is the main advantage of GraphQL over REST for mobile applications?
Flashcards
Question
What are the 4 levels of the Richardson Maturity Model?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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?
Click to reveal answer
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.
Click to reveal answer
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