API Design Best Practices | RESTful Architecture Guide
Design APIs that developers love. Consistent naming, proper versioning, clear error messages, and OpenAPI documentation for maintainable, evolvable interfaces.
6 min read
Good API design makes consumers' lives easier and reduces support burden. Bad API design leads to confusion, bugs, and frustrated developers. This guide covers practical approaches to API design that stand the test of time.
API Principles
| Principle | Description | Benefit |
|---|---|---|
| Consistency | Same patterns | Learnability |
| Predictability | Expected behavior | Less bugs |
| Evolvability | Change safely | Longevity |
| Simplicity | Easy to use | Adoption |
RESTful Design
Resource-Oriented API
RESTFUL API DESIGN
ββββββββββββββββββ
RESOURCES:
βββββββββββββββββββββββββββββββββββββ
Think in nouns, not verbs:
βββ /users (not /getUsers)
βββ /projects (not /createProject)
βββ /tasks (not /deleteTask)
βββ Resources are things
βββ HTTP methods are actions
CRUD OPERATIONS:
βββββββββββββββββββββββββββββββββββββ
HTTP Method β Operation β Example
βββββββββββββΌββββββββββββΌββββββββββββββββββ
GET β Read β GET /users
POST β Create β POST /users
PUT β Replace β PUT /users/123
PATCH β Update β PATCH /users/123
DELETE β Delete β DELETE /users/123
RESOURCE HIERARCHY:
βββββββββββββββββββββββββββββββββββββ
Nested resources:
βββ GET /projects/123/tasks
βββ POST /projects/123/tasks
βββ GET /projects/123/tasks/456
βββ Clear relationships
βββ Logical hierarchy
URL STRUCTURE:
βββββββββββββββββββββββββββββββββββββ
Consistent naming:
βββ Plural nouns: /users, /tasks
βββ Lowercase: /user-profiles
βββ Hyphens for multi-word
βββ No trailing slashes
βββ No file extensions
βββ Clean, predictable
Request/Response
Data Formatting
REQUEST/RESPONSE DESIGN
βββββββββββββββββββββββ
CONSISTENT JSON:
βββββββββββββββββββββββββββββββββββββ
Always same structure:
Success response:
{
"data": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
}
List response:
{
"data": [
{ "id": 123, "name": "John" },
{ "id": 124, "name": "Jane" }
],
"meta": {
"total": 100,
"page": 1,
"perPage": 20
}
}
NAMING CONVENTIONS:
βββββββββββββββββββββββββββββββββββββ
Consistent casing:
βββ camelCase for JSON: firstName
βββ snake_case also common: first_name
βββ Pick one, use everywhere
βββ Match your language conventions
βββ Consistency matters most
FIELD FILTERING:
βββββββββββββββββββββββββββββββββββββ
Let clients request fields:
βββ GET /users?fields=id,name,email
βββ Reduce over-fetching
βββ Better performance
βββ GraphQL approach for REST
βββ Optional but helpful
Error Handling
Clear Error Responses
ERROR HANDLING
ββββββββββββββ
ERROR RESPONSE FORMAT:
βββββββββββββββββββββββββββββββββββββ
Consistent error structure:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request was invalid",
"details": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "password",
"message": "Must be at least 8 characters"
}
]
}
}
HTTP STATUS CODES:
βββββββββββββββββββββββββββββββββββββ
Use correctly:
βββ 200: Success
βββ 201: Created
βββ 204: No content (delete success)
βββ 400: Bad request (client error)
βββ 401: Unauthorized (not logged in)
βββ 403: Forbidden (no permission)
βββ 404: Not found
βββ 409: Conflict (duplicate, etc.)
βββ 422: Unprocessable (validation)
βββ 500: Server error
βββ Meaningful status codes
ERROR MESSAGES:
βββββββββββββββββββββββββββββββββββββ
Good error messages:
βββ Human-readable message
βββ Machine-readable code
βββ Specific field errors
βββ Actionable guidance
βββ No stack traces in prod
βββ Helpful, not cryptic
β
"Email is already registered"
β "Error: duplicate key violation"
Pagination
Handling Large Lists
PAGINATION PATTERNS
βββββββββββββββββββ
OFFSET PAGINATION:
βββββββββββββββββββββββββββββββββββββ
GET /users?page=2&perPage=20
Response:
{
"data": [...],
"meta": {
"total": 500,
"page": 2,
"perPage": 20,
"totalPages": 25
}
}
Pros: Simple, familiar
Cons: Performance on large offsets
CURSOR PAGINATION:
βββββββββββββββββββββββββββββββββββββ
GET /users?cursor=abc123&limit=20
Response:
{
"data": [...],
"cursors": {
"next": "xyz789",
"previous": "abc122"
}
}
Pros: Better performance
Cons: Can't jump to page
BEST PRACTICE:
βββββββββββββββββββββββββββββββββββββ
βββ Default page size (20)
βββ Max page size (100)
βββ Include total count
βββ Include navigation links
βββ Document pagination approach
βββ Consistent across endpoints
Versioning
API Evolution
API VERSIONING
ββββββββββββββ
URL VERSIONING:
βββββββββββββββββββββββββββββββββββββ
/v1/users
/v2/users
Pros:
βββ Explicit and clear
βββ Easy to understand
βββ Good for caching
βββ Easy to test
βββ Most common
HEADER VERSIONING:
βββββββββββββββββββββββββββββββββββββ
Accept: application/vnd.api+json;version=1
Pros:
βββ Cleaner URLs
βββ Same resource, different representation
Cons:
βββ Less visible
βββ Harder to test
VERSIONING STRATEGY:
βββββββββββββββββββββββββββββββββββββ
βββ Don't version too early
βββ Version for breaking changes only
βββ Maintain old versions (6-12 months)
βββ Deprecation warnings
βββ Migration guides
βββ Clear sunset dates
βββ Plan for change
AVOIDING BREAKING CHANGES:
βββββββββββββββββββββββββββββββββββββ
Non-breaking changes:
βββ Add new fields (optional)
βββ Add new endpoints
βββ Add new enum values
βββ Relax validation
βββ Extend, don't change
Breaking changes (avoid):
βββ Remove fields
βββ Rename fields
βββ Change field types
βββ Require new fields
βββ Change behavior
βββ Requires new version
Documentation
API Documentation
API DOCUMENTATION
βββββββββββββββββ
OPENAPI/SWAGGER:
βββββββββββββββββββββββββββββββββββββ
Standard documentation:
βββ Machine-readable spec
βββ Auto-generated docs
βββ Try-it-out functionality
βββ Client generation
βββ Single source of truth
βββ Industry standard
DOCUMENTATION INCLUDES:
βββββββββββββββββββββββββββββββββββββ
For each endpoint:
βββ URL and method
βββ Description
βββ Request parameters
βββ Request body schema
βββ Response schemas
βββ Example requests
βββ Example responses
βββ Error codes
βββ Authentication
βββ Complete picture
GOOD EXAMPLES:
βββββββββββββββββββββββββββββββββββββ
Show real-world usage:
## Create User
POST /v1/users
Request:
{
"name": "John Doe",
"email": "john@example.com",
"password": "securePassword123"
}
Response: 201 Created
{
"data": {
"id": "user_abc123",
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
}
KEEP DOCS UPDATED:
βββββββββββββββββββββββββββββββββββββ
βββ Generate from code
βββ Part of build process
βββ CI validates spec
βββ Outdated docs are harmful
βββ Treat as first-class
Project Integration
API Tasks in GitScrum
GITSCRUM FOR API PROJECTS
βββββββββββββββββββββββββ
API TASK STRUCTURE:
βββββββββββββββββββββββββββββββββββββ
Task: "Create user registration endpoint"
βββ Acceptance criteria:
β βββ POST /v1/users
β βββ Validates email/password
β βββ Returns 201 with user data
β βββ Errors follow standard format
βββ Documentation updated
βββ Tests included
βββ Clear definition
TRACKING:
βββββββββββββββββββββββββββββββββββββ
βββ Label: api, endpoint
βββ Link to API spec
βββ Version tag
βββ Related frontend tasks
βββ Organized work
API DOCUMENTATION:
βββββββββββββββββββββββββββββββββββββ
βββ OpenAPI spec in repo
βββ Link in NoteVault
βββ Changes tracked
βββ Version history
βββ Findable documentation
Best Practices
For API Design
Anti-Patterns
API DESIGN MISTAKES:
β Inconsistent naming
β Breaking changes without version
β Generic error messages
β No pagination
β Returning entire database
β Outdated documentation
β No rate limiting
β Poor authentication