Skip to content

// Guide

REST API design best practices that survive implementation

Practical rules for resource paths, HTTP methods, validation, errors and change management.

·10 min read

Model resources, not interface screens

Resource paths should represent concepts in the domain rather than the current UI. A payment remains a payment whether it is created from a checkout screen, an admin tool or an automated job.

Use nouns for resource collections and stable identifiers for individual resources. Keep nesting shallow unless the parent is necessary to identify the child.

GET /orders/{orderId}/payments
POST /payments
GET /payments/{paymentId}

Use HTTP method semantics deliberately

GET retrieves without changing server state. POST creates or initiates processing. PUT replaces a known resource representation and should be idempotent. PATCH applies a partial change. DELETE removes the target and is expected to be idempotent from the caller's point of view.

These properties affect retries, caching and client behavior. Do not choose methods only because a framework route was convenient.

  • Never put required side effects behind GET.
  • Design PUT and DELETE so safe retries do not create extra effects.
  • Document whether POST supports an idempotency key.
  • Use PATCH semantics that are explicit about omitted and null fields.

Return useful status codes

Choose a status that explains the outcome before the caller parses the body. Use a consistent error envelope to carry a machine-readable code, human-readable message and field-level details.

Do not return 200 for every result. Transport success, domain rejection and server failure need different handling.

HTTP/1.1 422 Unprocessable Content

{
  "code": "validation_failed",
  "fields": { "amount": "Must be positive" }
}

Design filtering and pagination together

Collections grow. Define ordering, filters and pagination before a response becomes too large. Cursor pagination handles changing datasets better than page numbers when stable traversal matters.

Always define the default order. Without it, a cursor or page can produce inconsistent results across calls.

  • Document default and maximum page size.
  • Return a next cursor only when another page exists.
  • Keep cursor contents opaque to clients.
  • Name filters consistently across collections.

Treat compatibility as a design constraint

Adding an optional response field is usually compatible. Removing a field, changing its meaning or making an optional request field required can break callers.

Prefer additive evolution, deprecation notices and observable migration windows. Version only when compatibility cannot be preserved, and document the scope of the version boundary.

  • Track which changes affect request validation.
  • Regenerate client types after schema changes.
  • Test old and new callers during migrations.
  • Keep examples aligned with the supported version.

Product documentation

Related guides

// GET STARTED

Bring your API workflow into one workspace.

Start with an OpenAPI or Swagger JSON definition and keep the workflow together.

Start for free