Skip to content
S sufi.my
Back to Guides

Free Guide

Backend API Design Checklist

A practical checklist for designing clean, consistent, and production-ready REST APIs.

4 min read

Why a checklist?

API design decisions compound. A shortcut in naming or error handling early on creates inconsistency that spreads across every endpoint. This checklist helps you make deliberate choices before writing code.

URL Structure

  • Use nouns, not verbs: /users, /orders, not /getUsers, /createOrder
  • Use plural resource names: /users/123, not /user/123
  • Nest for relationships: /users/123/orders (orders belonging to user 123)
  • Keep URLs lowercase with hyphens: /order-items, not /orderItems
  • Limit nesting to 2 levels max: /users/123/orders is fine, /users/123/orders/456/items/789 is too deep
GET    /api/v1/users          # List users
GET    /api/v1/users/123      # Get user 123
POST   /api/v1/users          # Create user
PUT    /api/v1/users/123      # Replace user 123
PATCH  /api/v1/users/123      # Partial update
DELETE /api/v1/users/123      # Delete user 123

HTTP Methods

MethodPurposeIdempotentRequest Body
GETReadYesNo
POSTCreateNoYes
PUTFull replaceYesYes
PATCHPartial updateNoYes
DELETERemoveYesNo

Key rules:

  • GET must never modify state
  • POST is the only non-idempotent method — calling it twice creates two resources
  • PUT replaces the entire resource; PATCH updates specific fields only

Status Codes

Use the right code for the right situation:

200 OK           — Successful GET, PUT, PATCH, DELETE
201 Created      — Successful POST (include Location header)
204 No Content   — Successful DELETE with no response body
400 Bad Request  — Validation failed (include field-level errors)
401 Unauthorized — Missing or invalid authentication
403 Forbidden    — Authenticated but not authorized
404 Not Found    — Resource doesn't exist
409 Conflict     — Duplicate or state conflict
422 Unprocessable — Syntactically correct but semantically wrong
429 Too Many     — Rate limit exceeded (include Retry-After)
500 Server Error — Unhandled exception (never expose stack traces)

Error Response Format

Use a consistent error format across all endpoints:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Must be a valid email address"
      },
      {
        "field": "age",
        "message": "Must be at least 18"
      }
    ]
  }
}

Rules:

  • Always return a machine-readable code (not just the HTTP status)
  • Include field in validation errors so the UI can highlight the right input
  • Never expose internal errors, stack traces, or database details to the client

Pagination

For list endpoints, always paginate:

GET /api/v1/users?page=2&size=20

{
  "data": [...],
  "pagination": {
    "page": 2,
    "size": 20,
    "total": 156,
    "totalPages": 8
  }
}
  • Default to size=20 if not specified
  • Cap maximum page size (e.g., 100)
  • Include total count for UI pagination controls
  • For large datasets, consider cursor-based pagination instead

Versioning

  • Use URL path versioning: /api/v1/users
  • Increment the major version only for breaking changes
  • Keep old versions alive until all clients migrate
  • Document deprecation timelines

Authentication

  • Use Bearer tokens in the Authorization header: Authorization: Bearer <token>
  • Never pass tokens in URL query strings (they appear in logs)
  • Return 401 for missing/expired tokens, 403 for insufficient permissions
  • Include token expiry in the response so clients can refresh proactively

Request Validation

  • Validate all inputs on the server, even if the client validates too
  • Return 400 with field-level errors for invalid data
  • Reject unknown fields (don’t silently ignore them)
  • Set maximum sizes for strings, arrays, and file uploads
  • Sanitize inputs to prevent injection attacks

Response Best Practices

  • Use consistent naming: camelCase or snake_case, pick one and stick to it
  • Include id in every resource response
  • Return the created/updated resource in POST/PUT/PATCH responses
  • Use ISO 8601 for dates: "2026-01-15T10:30:00Z"
  • Wrap collections in a data array (not a bare array)

Rate Limiting

  • Set rate limits per endpoint or per user
  • Return 429 Too Many Requests when exceeded
  • Include headers: X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After
  • Use sliding window or token bucket algorithms

Quick Checklist

Before shipping any API endpoint, verify:

  • URL uses plural nouns, lowercase, hyphens
  • Correct HTTP method for the operation
  • Returns appropriate status code (not always 200)
  • Error responses follow consistent format
  • List endpoints are paginated
  • Inputs are validated server-side
  • Response includes resource ID
  • Dates use ISO 8601 format
  • Authentication is required (unless intentionally public)
  • Rate limiting is configured
  • No sensitive data in URLs or logs