Introduction to API Design
APIs (Application Programming Interfaces) are the contracts between different software components. Well-designed APIs are intuitive, consistent, and evolve gracefully over time.
REST Principles
REST (Representational State Transfer) is the most common API architectural style:
Resources
Everything is a resource identified by a URL:
/users- collection of users/users/123- specific user/users/123/orders- user's orders
HTTP Methods
Use HTTP methods semantically:
- GET: Retrieve resources (safe, idempotent)
- POST: Create new resources
- PUT: Replace entire resource (idempotent)
- PATCH: Partial update
- DELETE: Remove resource (idempotent)
Stateless
Each request contains all information needed. No server-side session state.
URL Design
Use Nouns, Not Verbs
Good: GET /users/123
Bad: GET /getUser?id=123
Use Plural Names
Good: /users, /orders, /products
Bad: /user, /order, /product
Nest Related Resources
GET /users/123/orders # User's orders
GET /orders/456/items # Order's items
Keep URLs Simple
Limit nesting depth to 2-3 levels:
Good: /users/123/orders
Bad: /users/123/orders/456/items/789/reviews
Request/Response Design
Use Proper Status Codes
200 OK - Success
201 Created - Resource created
204 No Content - Success, no body
400 Bad Request - Client error
401 Unauthorized - Authentication required
403 Forbidden - Not permitted
404 Not Found - Resource doesn't exist
409 Conflict - Resource conflict
422 Unprocessable - Validation error
500 Server Error - Something went wrong
Consistent Response Format
{
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"meta": {
"requestId": "abc-123"
}
}
Error Responses
Include helpful error details:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
Pagination
For large collections, implement pagination:
Offset-Based
GET /users?offset=0&limit=20
GET /users?offset=20&limit=20
Simple but problematic with changing data.
Cursor-Based
GET /users?cursor=abc123&limit=20
Better for real-time data. Cursor encodes position.
Response with Pagination Info
{
"data": [...],
"pagination": {
"total": 100,
"limit": 20,
"offset": 0,
"next": "/users?offset=20&limit=20"
}
}
Filtering and Sorting
Filtering
GET /users?status=active
GET /users?created_after=2024-01-01
GET /users?role=admin&status=active
Sorting
GET /users?sort=created_at
GET /users?sort=-created_at # Descending
GET /users?sort=name,-created_at # Multiple fields
Versioning
APIs evolve. Version them from the start:
URL Versioning
GET /v1/users
GET /v2/users
Most visible, easiest to use.
Header Versioning
GET /users
Accept: application/vnd.api+json; version=2
Cleaner URLs but harder to test.
Authentication
API Keys
Simple, good for server-to-server:
X-API-Key: your-api-key
JWT (JSON Web Tokens)
Stateless, good for user authentication:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
OAuth 2.0
Standard for delegated authorization. Complex but powerful.
Rate Limiting
Protect your API from abuse:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
Return 429 Too Many Requests when exceeded.
Documentation
Good documentation is essential:
- OpenAPI/Swagger: Machine-readable specification
- Examples: Show real requests and responses
- Error codes: Document all possible errors
- Changelog: Track breaking changes
Best Practices Summary
- Be consistent: Same patterns everywhere
- Use proper HTTP: Methods, status codes, headers
- Version from day one: Plan for evolution
- Document thoroughly: Examples, errors, changelog
- Validate input: Return helpful error messages
- Paginate collections: Don't return unbounded lists
- Rate limit: Protect against abuse
- Use HTTPS: Always encrypt in transit
Conclusion
Well-designed APIs make developers happy and systems more maintainable. Invest time upfront in thoughtful API design — it pays dividends as your system grows.