DEV Community

Alex Chen
Alex Chen

Posted on

Designing REST APIs That Developers Love

Designing REST APIs That Developers Love

I've consumed hundreds of APIs. Here's what separates the good from the painful.

1. Consistent URL Structure

✅ Good:                          ❌ Bad:
/api/users                        /getUsers
/api/users/123                    /user/get?id=123
/api/users/123/posts              /getUserPosts
/api/users/123/posts/456          /api/getPost?userId=123&postId=456

Rules:
- Nouns only (no verbs in URLs)
- Plural resource names
- Hierarchical (nested = relationship)
- kebab-case, not camelCase or snake_case
Enter fullscreen mode Exit fullscreen mode

2. Use HTTP Methods Correctly

// CRUD mapping:
GET    /api/users            List all users
GET    /api/users/123        Get specific user
POST   /api/users            Create new user
PUT    /api/users/123        Full update (replace)
PATCH  /api/users/123        Partial update
DELETE /api/users/123        Delete user

// Actions (when you need verbs):
POST   /api/users/123/activate     // Not GET /activate-user!
POST   /api/orders/123/cancel     // Not GET /cancel-order
POST   /api/posts/456/publish     // Not GET /publish-post
Enter fullscreen mode Exit fullscreen mode

3. Proper Status Codes (The Developer's Perspective)

// When I consume your API, here's what I expect:

// Success
200 OK                  // Standard response
201 Created             // POST success — include Location header
204 No Content          // DELETE success — no body needed

// Client errors (MY fault)
400 Bad Request         // My request is malformed — tell me what's wrong!
401 Unauthorized        // I need to authenticate
403 Forbidden           // I'm authenticated but not allowed
404 Not Found           // Resource doesn't exist
405 Method Not Allowed  // Wrong HTTP method for this endpoint
409 Conflict            // Duplicate resource, version conflict
422 Unprocessable       // Valid syntax but validation failed
429 Too Many Requests   // Slow down — include Retry-After header

// Server errors (YOUR fault)
500 Internal Error      // Something broke on your end
502 Bad Gateway         // Your upstream service is down
503 Unavailable         // You're in maintenance mode
Enter fullscreen mode Exit fullscreen mode

4. Pagination That Doesn't Suck

// ✅ Good pagination response:
GET /api/users?page=1&per_page=20

{
  "data": [...],
  "pagination": {
    "page": 1,
    "per_page": 20,
    "total": 142,
    "total_pages": 8,
    "has_next": true,
    "has_prev": false
  },
  "links": {
    "first": "/api/users?page=1&per_page=20",
    "prev": null,
    "next": "/api/users?page=2&per_page=20",
    "last": "/api/users?page=8&per_page=20"
  }
}

// Cursor-based (for infinite scroll):
GET /api/posts?cursor=abc123&limit=20

{
  "data": [...],
  "cursor": {
    "next": "def456",
    "has_more": true
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Filtering, Sorting, and Searching

// Filtering:
GET /api/users?role=admin&active=true&created_after=2026-01-01

// Sorting (allow multiple fields):
GET /api/users?sort=created_at&order=desc
GET /api/users?sort=name,age&order=asc,desc

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

// Search:
GET /api/users?q=alex&search_by=name,email
// Search across name and email fields

// Include related resources:
GET /api/users/123?include=posts,comments
// Embeds related data to avoid extra requests
Enter fullscreen mode Exit fullscreen mode

6. Error Responses That Help Me Debug

// ❌ Bad error response:
{
  "error": "Something went wrong"
}
// I have NO idea what went wrong!

// ✅ Good error response:
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format",
        "value": "not-an-email"
      },
      {
        "field": "password",
        "message": "Password must be at least 8 characters",
        "value": "short"
      }
    ],
    "request_id": "req_abc123", // For support lookup
    "docs": "https://api.example.com/docs/errors#validation"
  }
}
// Now I know EXACTLY what to fix!
Enter fullscreen mode Exit fullscreen mode

7. Versioning

// URL versioning (most common):
/api/v1/users
/api/v2/users

// Header versioning:
Accept: application/vnd.api.v2+json

// My recommendation: Start with v1 from day one.
// Even if it's your first release.
// You WILL break things later and wish you had versions.
Enter fullscreen mode Exit fullscreen mode

8. Authentication & Rate Limiting

// Always return rate limit info in headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1703980800

// Standard auth headers:
Authorization: Bearer eyJhbGciOi...
X-API-Key: api_key_abc123

// For OAuth2:
// POST /oauth/token → { access_token, token_type, expires_in }
// Then use access_token in Authorization header
Enter fullscreen mode Exit fullscreen mode

9. Async Operations

// Long-running tasks should return immediately:

POST /api/reports/generate
// 202 Accepted
{
  "job_id": "job_abc123",
  "status": "pending",
  "estimated_time": 30,
  "check_url": "/api/jobs/job_abc123"
}

// Check status:
GET /api/jobs/job_abc123
{
  "job_id": "job_abc123",
  "status": "completed",  // pending, running, completed, failed
  "result_url": "/api/reports/report_xyz",
  "progress": 100,
  "started_at": "2026-01-15T10:00:00Z",
  "completed_at": "2026-01-15T10:00:28Z"
}
Enter fullscreen mode Exit fullscreen mode

10. Documentation (Non-Negotiable)

// What every API needs:

// 1. OpenAPI/Swagger spec (machine-readable)
// https://api.example.com/swagger.json
// or https://api.example.com/docs (Swagger UI)

// 2. Quick start guide (5 minutes to first request)
// Show a real cURL example with real data

// 3. Realistic examples for EVERY endpoint
// Include request AND response bodies

// 4. Error code reference
// List all possible errors with solutions

// 5. Interactive playground
// Swagger UI, Postman collection, or Insomnia
Enter fullscreen mode Exit fullscreen mode

What API design rule do you swear by? What's the worst API you've used?

Follow @armorbreak for more backend content.

Top comments (0)