DEV Community

Alex Chen
Alex Chen

Posted on

Designing REST APIs That Developers Actually Love Using

Designing REST APIs That Developers Actually Love Using

Good APIs don't just work — they're a joy to use.

URL Design

# ✅ Good: Nouns, not verbs
GET    /api/users           # List users
POST   /api/users           # Create user
GET    //api/users/123      # Get specific user
PUT    /api/users/123       # Update (full)
PATCH  /api/users/123       # Update (partial)
DELETE /api/users/123       # Delete user

# ❌ Bad: Verbs in URLs, inconsistent naming
GET    /api/getUsers
POST   /api/createUser
GET    /api/user/get?id=123
DELETE /api/removeUser/123
Enter fullscreen mode Exit fullscreen mode

Resource Naming Conventions

# Plural nouns for collections
/api/orders          # Not /api/order or /api/orderList

# Nested resources show relationships
/api/users/123/orders              # Orders for user 123
/api/users/123/orders/456          # Specific order of specific user

# Keep URLs shallow (max 2-3 levels)
# Too deep: /api/orgs/5/depts/3/teams/2/members/1
# Better:   /api/members/1?org=5&dept=3&team=2
Enter fullscreen mode Exit fullscreen mode

Query Parameters

# Pagination
GET /api/users?page=1&limit=20

# Filtering
GET /api/products?category=electronics&price_min=50&in_stock=true

# Sorting
GET /api/users?sort=created_at&order=desc
GET /api/users?sort=-created_at,name     # - prefix = descending

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

# Search
GET /api/users?q=alex&search_by=name,email

# Include related resources
GET /api/posts/123?include=author,comments,tags
Enter fullscreen mode Exit fullscreen mode

Response Format

// Standard success response
{
  "data": {
    "id": "usr_abc123",
    "name": "Alex Chen",
    "email": "alex@example.com",
    "role": "admin",
    "created_at": "2026-01-15T00:00:00Z",
    "updated_at": "2026-05-16T07:00:00Z"
  },
  "meta": {
    "request_id": "req_xyz789"
  }
}

// List response with pagination
{
  "data": [/* ... items ... */],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 150,
    "total_pages": 8,
    "has_next": true,
    "has_prev": false
  },
  "links": {
    "first": "/api/users?page=1",
    "prev": null,
    "next": "/api/users?page=2",
    "last": "/api/users?page=8"
  }
}

// Error response (ALWAYS consistent!)
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      { "field": "email", "message": "Invalid email format" },
      { "field": "password", "message": "Must be at least 8 characters" }
    ],
    "request_id": "req_xyz789"
  }
}
Enter fullscreen mode Exit fullscreen mode

HTTP Methods and Status Codes

Action Method Success Created Not Found Error
List GET 200 + array 404 (collection) 400/500
Read GET 200 + object 404 400/500
Create POST 201 + object 409 (duplicate) 400/422
Update PUT/PATCH 200 + object 404 400/422
Delete DELETE 200/204 404 500

Versioning

# Option 1: URL path (most common)
/api/v1/users
/api/v2/users

# Option 2: Header (cleaner URLs)
Accept: application/vnd.api.v1+json
GET /api/users

# Option 3: Query parameter (simplest)
GET /api/users?version=v1
Enter fullscreen mode Exit fullscreen mode

Authentication

// Bearer token (JWT)
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

// API key (for server-to-server)
X-API-Key: sk_live_abc123

// Basic auth (rarely used for APIs)
Authorization: Basic YWxleDpwYXNzd29yZA==
Enter fullscreen mode Exit fullscreen mode

Rate Limiting Headers

HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 997
X-RateLimit-Reset: 1715841200
Retry-After: 60
Enter fullscreen mode Exit fullscreen mode

Idempotency Keys (For Safe Retries)

// Client generates unique key
const idempotencyKey = crypto.randomUUID();

const response = await fetch('/api/charges', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey,
  },
  body: JSON.stringify({ amount: 1000 }),
});

// If request fails and client retries with same key:
// Server returns the original result instead of creating a duplicate!
Enter fullscreen mode Exit fullscreen mode

Quick Checklist

□ Use plural nouns for resource names
□ Consistent naming (snake_case or camelCase, pick one!)
□ Paginate all list endpoints
□ Return proper HTTP status codes
□ Include error details in error responses
□ Support filtering, sorting, field selection
□ Add rate limiting headers
□ Document with OpenAPI/Swagger
□ Use HTTPS everywhere
□ Validate ALL input server-side
□ Include request IDs for debugging
□ Make operations idempotent where possible
Enter fullscreen mode Exit fullscreen mode

What's your biggest API pet peeve?

Follow @armorbreak for more API content.

Top comments (0)