Skip to main content
โšก Calmops

REST API Design Best Practices: A Complete Guide

REST API Design Best Practices: A Complete Guide

REST (Representational State Transfer) APIs are the backbone of modern web services. This comprehensive guide covers everything you need to design scalable, maintainable, and developer-friendly REST APIs.

What is REST?

REST is an architectural style for designing networked applications. It relies on a stateless, client-server communication protocolโ€”almost always HTTP.

Core Principles

  • Client-Server Architecture: Separation of concerns between client and server
  • Statelessness: Each request contains all information needed
  • Cacheability: Responses can be cached for performance
  • Uniform Interface: Consistent resource identification
  • Layered System: Scalable architecture through intermediaries

Resource Naming Conventions

The foundation of good REST API design is proper resource naming. Resources should be nouns, not verbs.

Good vs Bad Examples

# Good - Nouns representing resources
GET    /users
GET    /users/123
GET    /users/123/orders
POST   /orders

# Bad - Verbs or unclear naming
GET    /getUsers
GET    /userInfo
POST   /createOrder
GET    /fetchUserData/123

Resource Naming Rules

# Use plural nouns for collections
GET    /products          # Collection of products
GET    /products/456      # Single product

# Use lowercase with hyphens for multi-word resources
GET    /user-profiles
GET    /order-items

# Use nesting to show relationships
GET    /users/123/orders          # User's orders
GET    /orders/789/items          # Order's items
GET    /users/123/orders/456      # Specific order

# Avoid deep nesting (max 2-3 levels)
# Good
GET    /users/123/orders

# Too deep - consider flattening
GET    /orders?userId=123

Concrete Examples

# Blog API example
GET    /posts                    # List all posts
GET    /posts/123                # Single post
GET    /posts/123/comments       # Comments on a post
POST   /posts/123/comments       # Add comment to post
GET    /authors                  # List all authors
GET    /authors/456/posts        # Author's posts

# E-commerce API example  
GET    /products                 # List products
GET    /products?category=books  # Filter by category
GET    /products?sort=price_asc  # Sort products
GET    /products?page=2&limit=20 # Pagination
POST   /carts                   # Create cart
PUT    /carts/123/items         # Update cart
DELETE /carts/123/items/456     # Remove item

HTTP Methods

Use HTTP methods semantically to indicate action:

Method Purpose Idempotent
GET Retrieve resources Yes
POST Create new resources No
PUT Replace entire resource Yes
PATCH Partial update No
DELETE Remove resource Yes

GET - Retrieve Resources

# Get all users
GET /users

# Get single user
GET /users/123

# Get with filtering
GET /users?status=active&role=admin

# Get with sorting
GET /users?sort=created_at,desc

# Get with pagination
GET /users?page=2&limit=20

POST - Create Resources

# Create new user
POST /users
Content-Type: application/json

{
  "name": "John Doe",
  "email": "[email protected]",
  "role": "developer"
}

# Response
HTTP/1.1 201 Created
Location: /users/456

PUT vs PATCH

# PUT - Replace entire resource
PUT /users/123
Content-Type: application/json

{
  "name": "John Doe",
  "email": "[email protected]",
  "role": "admin",
  "status": "active"
}

# PATCH - Partial update
PATCH /users/123
Content-Type: application/json

{
  "role": "admin"
}

DELETE - Remove Resources

# Delete single resource
DELETE /users/123

# Delete multiple
DELETE /users?status=inactive

HTTP Status Codes

Use appropriate status codes to indicate the result of the request:

Success Codes (2xx)

# 200 OK - Request successful
GET /users/123

# 201 Created - Resource successfully created
POST /users
Location: /users/456

# 204 No Content - Successful but no content to return
DELETE /users/123

Client Error Codes (4xx)

# 400 Bad Request - Invalid request syntax
POST /users
{
  "email": "not-an-email"  # Invalid format
}

# 401 Unauthorized - Authentication required
GET /protected-resource
# Missing Authorization header

# 403 Forbidden - Authenticated but not authorized
GET /admin/users
# User doesn't have admin role

# 404 Not Found - Resource doesn't exist
GET /users/999999

# 409 Conflict - Resource conflict
POST /users
{
  "email": "[email protected]"  # Email already exists
}

# 422 Unprocessable Entity - Valid syntax but semantic errors
POST /users
{
  "name": ""  # Required field empty
}

# 429 Too Many Requests - Rate limited

Server Error Codes (5xx)

# 500 Internal Server Error
# 502 Bad Gateway
# 503 Service Unavailable
# 504 Gateway Timeout

Complete Status Code Reference

# 1xx Informational
100 - Continue
101 - Switching Protocols

# 2xx Success
200 - OK
201 - Created
202 - Accepted
204 - No Content

# 3xx Redirection
301 - Moved Permanently
302 - Found
304 - Not Modified

# 4xx Client Errors
400 - Bad Request
401 - Unauthorized
403 - Forbidden
404 - Not Found
405 - Method Not Allowed
409 - Conflict
422 - Unprocessable Entity
429 - Too Many Requests

# 5xx Server Errors
500 - Internal Server Error
502 - Bad Gateway
503 - Service Unavailable
504 - Gateway Timeout

Error Handling

A well-designed error response helps developers debug issues quickly.

Standard Error Response Format

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request parameters",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format",
        "value": "not-an-email"
      },
      {
        "field": "age",
        "message": "Must be greater than 0",
        "value": "-5"
      }
    ],
    "requestId": "req_abc123xyz"
  }
}

Error Response Examples

// Resource not found
{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "User with ID 123 not found",
    "resource": "User",
    "resourceId": "123"
  }
}

// Authentication failure
{
  "error": {
    "code": "AUTHENTICATION_FAILED",
    "message": "Invalid API key",
    "help": "Provide a valid Authorization header"
  }
}

// Rate limiting
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Too many requests",
    "retryAfter": 60,
    "limit": 100,
    "remaining": 0
  }
}

Best Practices for Error Handling

# Good: Consistent error structure across all endpoints
{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable message",
    "details": []  # Optional array for validation errors
  }
}

# Bad: Inconsistent error formats
{"message": "Error"}                    # Wrong
{"error": "Something went wrong"}       # Wrong
{"status": "error", "msg": "Failed"}   # Wrong

Pagination

Always paginate endpoints that return collections:

Offset-Based Pagination

GET /users?page=1&limit=20
{
  "data": [
    {"id": 1, "name": "User 1"},
    {"id": 2, "name": "User 2"}
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "totalPages": 8
  }
}

Cursor-Based Pagination

GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
{
  "data": [
    {"id": 101, "name": "User 101"},
    {"id": 102, "name": "User 102"}
  ],
  "pagination": {
    "nextCursor": "eyJpZCI6MTIwfQ",
    "previousCursor": "eyJpZCI6OTB9",
    "hasMore": true
  }
}

When to Use Each

  • Offset: When users need to jump to specific pages
  • Cursor: When dealing with real-time data or large datasets

Filtering, Sorting, and Field Selection

Filtering

# Single filter
GET /users?status=active

# Multiple filters
GET /users?status=active&role=admin

# Range filters
GET /products?price_min=10&price_max=100
GET /orders?created_after=2024-01-01

# In filters
GET /users?role_in=admin,moderator

# Exclude filters  
GET /users?status_ne=deleted

Sorting

# Single sort
GET /users?sort=created_at

# Descending sort
GET /users?sort=created_at,desc

# Multiple sort fields
GET /users?sort=status,asc;created_at,desc

Field Selection

# Return only specific fields
GET /users?fields=id,name,email

# Exclude specific fields
GET /users?exclude=password,token

API Versioning

Version your API to allow for breaking changes:

# Version in path
GET /v1/users
GET /v2/users

# Response header
Accept: application/vnd.myapp.v2+json

Query Parameter Versioning

GET /users?version=2

Header Versioning

GET /users
Header: X-API-Version: 2

Best Practices Summary

Do’s

  • Use nouns for resource names
  • Use proper HTTP methods
  • Return appropriate status codes
  • Implement pagination for collections
  • Version your API
  • Use consistent error formats
  • Document your API
  • Implement rate limiting

Don’ts

  • Use verbs in URLs
  • Return 200 for errors
  • Expose sensitive data in errors
  • Use generic error messages
  • Skip versioning
  • Return different structures for same status code

External Resources


Comments