Chuyển tới nội dung chính

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

MethodSafeIdempotentUse Case
GETRetrieve resource
POSTCreate resource
PUTReplace resource
PATCHUpdate resource
DELETEDelete 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

AspectRESTGraphQL
Over-fetchingCommonNo
Under-fetchingMultiple requestsSingle request
CachingBuilt-in (HTTP)Manual
ComplexitySimpleSteep 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

  1. Design a URL shortener API
  2. Design a rate limiter
  3. Design a webhook system

Trade-off Discussions

  1. REST vs GraphQL for your use case?
  2. When to use SOAP vs REST?
  3. How to handle API versioning?