REST API Design That Developers Actually Like
On this page
REST isn’t a library. It’s not a framework. It’s a set of conventions for designing web APIs. Follow them and any developer can guess how your API works without reading documentation. Break them and everyone has a bad time.
Resources, Not Actions
The single most important REST principle: think in nouns, not verbs.

Your API exposes “things” (users, posts, orders), not “actions” (createUser, deletePost).
Bad: POST /createUser, GET /getUserById, POST /deleteUser
Good: POST /users, GET /users/42, DELETE /users/42
The HTTP method already tells you the action. GET reads. POST creates. PUT replaces. DELETE removes. The URL just identifies WHICH resource.
URL Design That Reads Like English
Good REST URLs tell a story:
GET /users/42/postsmeans “get posts belonging to user 42”POST /teams/5/membersmeans “add a member to team 5”DELETE /orders/99/items/3means “remove item 3 from order 99”
Conventions:
- Pluralize resource names:
/usersnot/user - Lowercase with hyphens if needed:
/user-profilesnot/userProfiles - Keep nesting shallow (max 2-3 levels)
- No verbs in URLs ever
Query Parameters for Everything Else
Route params identify WHICH resource. Query params modify HOW you get it:
GET /users?role=admin filtering
GET /posts?sort=createdAt&order=desc sorting
GET /products?page=2&limit=20 pagination
GET /users?fields=id,name,email sparse fields
GET /events?after=2024-01-01 date ranges
Response Formats

Be consistent across your entire API. Pick a format and stick with it:
Single resource:
{
"id": 42,
"name": "Shubham",
"email": "shubham@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
Collection with pagination:
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 156,
"totalPages": 8
}
}
Error:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{"field": "email", "message": "must be valid"}
]
}
}
The key: a developer using your API should never be surprised by the shape of a response. Every endpoint follows the same patterns.
Versioning
APIs evolve. You’ll add fields, deprecate endpoints, change behavior. How do you do this without breaking apps that already use your API?
URL versioning (most common, most explicit):
/api/v1/users
/api/v2/users
Start with v1. Only bump the version when you make a BREAKING change. Adding new optional fields is not breaking. Removing a field or changing its type is.
Idempotency: Why It Matters
An operation is idempotent if calling it multiple times gives the same result as calling it once.
- GET: always idempotent (reading doesn’t change state)
- PUT: idempotent (replacing with same data = same result)
- DELETE: idempotent (deleting twice = still deleted)
- POST: NOT idempotent (creating twice = two resources)
This matters because networks are unreliable. A POST request times out. Did it succeed? The client doesn’t know. If they retry and it’s not idempotent, you get a duplicate. That’s why payment APIs use idempotency keys: “I already processed this exact request, here’s the same result again.”
Wrapping Up
REST is about conventions that make your API predictable:
- Resources as nouns, methods as verbs
- Consistent URL structure:
/resources/:id - Query params for filtering, sorting, pagination
- Consistent response shapes across all endpoints
- Version your API with URL prefix
- Understand idempotency for reliable retries
- Be predictable. Consistency beats cleverness.
Day 9 of 95 | Backend Engineering Series