DTTooleras

REST API Design: Best Practices, Naming Conventions, and Error Handling

A comprehensive guide to designing clean, consistent REST APIs — URL structure, HTTP methods, status codes, pagination, filtering, versioning, error responses, and authentication patterns.

DevToolsHub Team24 min read748 words

What Makes a Good REST API?

A well-designed REST API is:

  • Predictable — Developers can guess endpoints without reading docs
  • Consistent — Same patterns everywhere
  • Self-documenting — URLs describe the resource
  • Versioned — Changes don't break existing clients

URL Structure

Use Nouns, Not Verbs

✅ Good:
GET    /api/users          — List users
GET    /api/users/123      — Get user 123
POST   /api/users          — Create user
PUT    /api/users/123      — Update user 123
DELETE /api/users/123      — Delete user 123

❌ Bad:
GET    /api/getUsers
POST   /api/createUser
PUT    /api/updateUser/123
DELETE /api/deleteUser/123

The HTTP method already tells you the action. The URL should describe the resource.

Use Plural Nouns

✅ /api/users
✅ /api/posts
✅ /api/orders

❌ /api/user
❌ /api/post

Nested Resources

GET /api/users/123/orders          — Orders for user 123
GET /api/users/123/orders/456      — Order 456 for user 123
POST /api/users/123/orders         — Create order for user 123

Rule: Don't nest more than 2 levels deep. Beyond that, use query parameters or top-level endpoints.

Use Kebab-Case

✅ /api/user-profiles
✅ /api/order-items

❌ /api/userProfiles
❌ /api/order_items

HTTP Methods

MethodPurposeIdempotentRequest Body
GETRead resource(s)YesNo
POSTCreate resourceNoYes
PUTReplace resourceYesYes
PATCHPartial updateYesYes
DELETEDelete resourceYesNo

PUT vs PATCH

// PUT — Replace the entire resource
PUT /api/users/123
{
  "name": "Alice Updated",
  "email": "alice@new.com",
  "age": 31,
  "role": "admin"
}

// PATCH — Update only specified fields
PATCH /api/users/123
{
  "age": 31
}

Response Format

Consistent JSON Structure

// Success response
{
  "data": {
    "id": 123,
    "name": "Alice",
    "email": "alice@example.com"
  }
}

// List response with pagination
{
  "data": [
    { "id": 1, "name": "Alice" },
    { "id": 2, "name": "Bob" }
  ],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 150,
    "total_pages": 8
  }
}

// Error response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format" },
      { "field": "age", "message": "Must be between 0 and 150" }
    ]
  }
}

Status Codes

Use the right status code for every response:

200 OK              — Successful GET, PUT, PATCH
201 Created         — Successful POST (include Location header)
204 No Content      — Successful DELETE
400 Bad Request     — Invalid input
401 Unauthorized    — Not authenticated
403 Forbidden       — Not authorized
404 Not Found       — Resource doesn't exist
409 Conflict        — Duplicate resource
422 Unprocessable   — Validation errors
429 Too Many Reqs   — Rate limited
500 Server Error    — Something broke

Look up any status code with our HTTP Status Code Lookup tool.

Pagination

Offset-Based (Simple)

GET /api/users?page=2&per_page=20

Response:
{
  "data": [...],
  "pagination": {
    "page": 2,
    "per_page": 20,
    "total": 150,
    "total_pages": 8,
    "has_next": true,
    "has_prev": true
  }
}

Cursor-Based (Better for Large Datasets)

GET /api/users?cursor=eyJpZCI6MTAwfQ&limit=20

Response:
{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTIwfQ",
    "has_more": true
  }
}

Filtering, Sorting, and Searching

# Filtering
GET /api/users?status=active&role=admin

# Sorting
GET /api/users?sort=created_at&order=desc
GET /api/users?sort=-created_at  (prefix - for descending)

# Searching
GET /api/users?search=alice

# Field selection (sparse fieldsets)
GET /api/users?fields=id,name,email

# Date ranges
GET /api/orders?created_after=2024-01-01&created_before=2024-12-31

# Combined
GET /api/users?status=active&sort=-created_at&page=1&per_page=20&fields=id,name

Versioning

URL Versioning (Most Common)

GET /api/v1/users
GET /api/v2/users

Header Versioning

GET /api/users
Accept: application/vnd.myapi.v2+json

Recommendation: Use URL versioning. It's the most visible and easiest to implement.

Authentication

See our detailed guide: API Authentication Methods Compared

Quick summary:

  • Public APIs: API keys in header (X-API-Key)
  • User-facing APIs: JWT Bearer tokens (Authorization: Bearer <token>)
  • Third-party access: OAuth 2.0

Decode and inspect tokens with our JWT Decoder.

Rate Limiting

Always include rate limit headers:

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640000000

HTTP/1.1 429 Too Many Requests
Retry-After: 60

CORS

If your API is called from browsers, configure CORS properly. See our guide: Understanding CORS Errors

Related Tools

rest apiapi designapi best practicesrest api designapi endpointshttp methodsapi versioning

Related articles

All articles

Practice with free tools

200+ free developer tools that run in your browser.

Browse all tools →