API Design
📚 Overview
APIs (Application Programming Interfaces) bridge services. Good API design provides:
- Easy to maintain and evolve
- Developer-friendly
- Consistent behavior
- Good performance
🎯 RESTful API Design
Resource Naming
Best Practices:
| Pattern | ❌ Avoid | ✅ Use |
|---|---|---|
| Nouns | /getUsers | /users |
| Plural | /user | /users |
| Nesting | /users/1 | /users/{id} |
| Filtering | /users?active=true | /active-users |
HTTP Methods
| Method | Safe | Idempotent | Use Case |
|---|---|---|---|
| GET | ✅ | ✅ | Retrieve resource |
| POST | ❌ | ❌ | Create resource |
| PUT | ❌ | ✅ | Replace resource |
| PATCH | ❌ | ❌ | Update resource |
| DELETE | ❌ | ✅ | Delete resource |
Status Codes
// Success
200 OK // GET, PUT, PATCH
201 Created // POST
204 No Content // DELETE
// Client Error
400 Bad Request // Invalid input
401 Unauthorized // Not authenticated
403 Forbidden // Authenticated but not authorized
404 Not Found // Resource doesn't exist
409 Conflict // Duplicate, state conflict
422 Unprocessable // Valid syntax but semantic errors
429 Too Many Requests // Rate limit
// Server Error
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
Request/Response Design
// Good: Consistent structure
interface ApiResponse<T> {
data: T;
meta?: {
page: number;
pageSize: number;
total: number;
};
errors?: ApiError[];
}
interface ApiError {
code: string;
message: string;
field?: string;
}
// Example
GET /api/users?page=1&pageSize=20
{
"data": [...],
"meta": {
"page": 1,
"pageSize": 20,
"total": 100
}
}
🔄 Pagination
Offset-based
GET /api/users?offset=0&limit=20
Pros: Simple to implement Cons: Poor performance with large offsets, inconsistent data
Cursor-based
GET /api/users?cursor=abc123&limit=20
{
"data": [...],
"nextCursor": "def456"
}
Pros: Consistent, efficient for infinite scroll Cons: Cannot jump to specific page
🔐 Authentication & Authorization
JWT (JSON Web Token)
// Access Token (short-lived, 15 min)
{
"sub": "user-id",
"exp": 1234567890,
"permissions": ["read:users", "write:users"]
}
// Refresh Token (long-lived, 7 days)
{
"sub": "user-id",
"exp": 1234567890,
"type": "refresh"
}
Flow:
API Keys
// Header
X-API-Key: sk_live_xxxxx
// Or query param (less secure)
?api_key=sk_live_xxxxx
Best Practices:
- Prefix with environment:
sk_live_,sk_test_ - Rotate periodically
- Use HTTP Basic for simple cases
📝 Versioning
URL Versioning
/api/v1/users
/api/v2/users
Pros: Clear, easy to understand Cons: Duplicate routes, maintain multiple versions
Header Versioning
GET /api/users
Accept: application/vnd.myapi.v2+json
Pros: Clean URLs Cons: Less discoverable
🚀 Performance Optimization
Compression
// Response compression (gzip, brotli)
Accept-Encoding: gzip, deflate, br
ETag & Caching
// Response
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
// Next request
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
// Response: 304 Not Modified
Rate Limiting
// Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1634567890
// Retry-After header on 429
Retry-After: 60
🐛 Error Handling
// Good: Structured errors
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
],
"requestId": "req_abc123"
}
}
// Bad: Generic error
{
"error": "Something went wrong"
}
🔍 GraphQL (Optional)
vs REST
| Aspect | REST | GraphQL |
|---|---|---|
| Over-fetching | Common | No |
| Under-fetching | Multiple requests | Single request |
| Caching | Built-in (HTTP) | Manual |
| Complexity | Simple | Steep learning curve |
Query Example
# Query
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts(limit: 5) {
id
title
}
}
}
# Mutation
mutation CreatePost($input: PostInput!) {
createPost(input: $input) {
id
title
createdAt
}
}
❓ Interview Questions
Design Questions
- Design a URL shortener API
- Design a rate limiter
- Design a webhook system
Trade-off Discussions
- REST vs GraphQL for your use case?
- When to use SOAP vs REST?
- How to handle API versioning?