REST (Representational State Transfer) is an architectural style for building web APIs. It uses standard HTTP methods and status codes to provide a predictable, stateless interface for clients to interact with server resources.
Core REST Principles
REST APIs are built around a few key constraints:
- Stateless — Each request contains all the information the server needs. No session state is stored on the server between requests.
- Resource-based — Everything is a resource identified by a URL (e.g.,
/users/42). - Uniform interface — Use standard HTTP methods and status codes consistently.
- Client-server separation — The client and server evolve independently.
HTTP Methods
Each HTTP method maps to a specific operation on a resource:
| Method | Purpose | Idempotent | Safe |
|---|---|---|---|
| GET | Retrieve a resource | Yes | Yes |
| POST | Create a new resource | No | No |
| PUT | Replace a resource entirely | Yes | No |
| PATCH | Partially update a resource | Yes | No |
| DELETE | Remove a resource | Yes | No |
Idempotent means calling it multiple times produces the same result. Safe means it doesn't modify the resource.
Status Codes
Use the correct HTTP status code for every response. Here are the most important ones:
Success (2xx)
// 200 OK — Successful GET, PUT, PATCH, or DELETE
res.status(200).json({ id: 1, name: "Alice" });
// 201 Created — Successful POST that created a resource
res.status(201).json({ id: 2, name: "Bob" });
// 204 No Content — Successful DELETE with no response body
res.status(204).send();Client Errors (4xx)
// 400 Bad Request — Invalid input
res.status(400).json({ error: "Email is required" });
// 401 Unauthorized — Missing or invalid authentication
res.status(401).json({ error: "Invalid token" });
// 403 Forbidden — Authenticated but not authorized
res.status(403).json({ error: "Admin access required" });
// 404 Not Found — Resource doesn't exist
res.status(404).json({ error: "User not found" });
// 409 Conflict — Resource already exists
res.status(409).json({ error: "Email already registered" });
// 422 Unprocessable Entity — Validation failed
res.status(422).json({ errors: [{ field: "email", message: "Invalid format" }] });Server Errors (5xx)
// 500 Internal Server Error — Unexpected server failure
res.status(500).json({ error: "Something went wrong" });Resource Design
Good URL design is fundamental to a clean API. Resources should be nouns, not verbs.
# Good — resource-oriented
GET /api/users # List all users
GET /api/users/42 # Get user 42
POST /api/users # Create a user
PUT /api/users/42 # Replace user 42
PATCH /api/users/42 # Update user 42
DELETE /api/users/42 # Delete user 42
# Nested resources
GET /api/users/42/posts # List posts by user 42
POST /api/users/42/posts # Create a post for user 42
# Bad — verb-oriented (avoid this)
GET /api/getUsers
POST /api/createUser
POST /api/deleteUser/42Filtering, Sorting, and Pagination
Use query parameters for filtering and pagination, never path segments:
# Filtering
GET /api/users?role=admin&active=true
# Sorting
GET /api/users?sort=createdAt&order=desc
# Pagination
GET /api/users?page=2&limit=20JSON API Response Structure
Keep your response format consistent across all endpoints:
// Single resource
{
"data": {
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2026-03-12T10:00:00Z"
}
}
// Collection with pagination
{
"data": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
],
"meta": {
"total": 50,
"page": 1,
"limit": 20,
"totalPages": 3
}
}
// Error response
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input",
"details": [
{ "field": "email", "message": "Must be a valid email address" }
]
}
}Versioning
Always version your API to avoid breaking changes for existing clients:
# URL versioning (most common)
GET /api/v1/users
GET /api/v2/users
# Header versioning
GET /api/users
Accept: application/vnd.myapp.v2+jsonURL-based versioning is simpler and more explicit. Start with /api/v1/ from day one.