DEV Community

Alex Chen
Alex Chen

Posted on

Designing REST APIs That Developers Actually Love Using

Designing REST APIs That Developers Actually Love Using

A good API is invisible. A bad API ruins your day. Here's how to design the former.

The REST Basics

REST = REpresentational State Transfer

Core principles:
- Resources are nouns (users, posts, orders)
- HTTP methods are verbs (GET, POST, PUT, DELETE)
- Stateless — each request contains all info needed
- Uniform interface — consistent patterns everywhere
Enter fullscreen mode Exit fullscreen mode

URL Design

# ✅ Good: Resource-based, nested for relationships
GET    /api/users                    # List users
GET    /api/users/123                # Get user 123
POST   /api/users                    # Create user
PUT    /api/users/123                # Update user 123 (full)
PATCH  /api/users/123                # Update user 123 (partial)
DELETE /api/users/123                # Delete user 123

GET    /api/users/123/posts          # Posts by user 123
POST   /api/users/123/posts          # Create post for user 123
GET    /api/posts/456/comments       # Comments on post 456

# ❌ Bad: Verb-based URLs, inconsistent patterns
GET    /api/getUsers
POST   /api/createUser
GET    /api/user/list
POST   /api/User/create
DELETE /api/deleteUser?id=123
Enter fullscreen mode Exit fullscreen mode

Query Parameters

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

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

# Sorting
GET /api/users?sort=created_at&order=desc

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

# Search
GET /api/users?q=john&search=name,email

# Combined
GET /api/users?role=admin&page=1&limit=20&sort=created_at&fields=id,name,email
Enter fullscreen mode Exit fullscreen mode

Response Format

//  Consistent envelope
{
  "data": [
    { "id": 1, "name": "Alex", "email": "alex@example.com" },
    { "id": 2, "name": "Sarah", "email": "sarah@example.com" }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 156,
    "totalPages": 8
  }
}

//  Single resource
{
  "data": { "id": 1, "name": "Alex", "email": "alex@example.com" }
}

//  Error response
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": [
      { "field": "email", "message": "Must be a valid email" },
      { "field": "name", "message": "Must be at least 2 characters" }
    ]
  }
}

//  Inconsistent responses
{ "users": [...] }                    // Sometimes "users"
{ "data": [...] }                     // Sometimes "data"
{ "items": [...] }                    // Sometimes "items"
{ "error": "bad request" }            // No details
Enter fullscreen mode Exit fullscreen mode

Status Codes (Use Them Correctly!)

// Success
200 OK                // Standard response
201 Created           // Resource created (POST)
204 No Content        // Success, no body (DELETE)

// Client Errors
400 Bad Request       // Invalid request body/params
401 Unauthorized      // Not authenticated
403 Forbidden         // Authenticated but not authorized
404 Not Found         // Resource doesn't exist
409 Conflict          // Duplicate resource
422 Unprocessable     // Validation errors
429 Too Many Requests // Rate limited

// Server Errors
500 Internal Error    // Something broke
502 Bad Gateway       // Upstream server issue
503 Unavailable       // Server overloaded/maintenance

// ❌ Don't do this:
// Always returning 200 with error in body
{ "status": "error", "message": "Not found" }  // HTTP 200! Wrong!
Enter fullscreen mode Exit fullscreen mode

Versioning

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

# Option 2: Header
GET /api/users
Accept: application/vnd.myapi.v2+json

# Option 3: Query parameter (least recommended)
GET /api/users?version=2

# My recommendation: URL path — clearest, easiest to implement
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1715841200
Retry-After: 42

HTTP/1.1 429 Too Many Requests
Retry-After: 60
Enter fullscreen mode Exit fullscreen mode

Quick Reference

Method Path Purpose Returns
GET /resource List Array
GET /resource/:id Read Object
POST /resource Create 201 + Object
PUT /resource/:id Replace Object
PATCH /resource/:id Update Object
DELETE /resource/:id Delete 204

What API design patterns do you follow? Any pet peeves?

Follow @armorbreak for more backend content.

Top comments (0)