What is REST?
REST is a set of guidelines for building APIs. It's not a protocol or standard—it's an architectural style. An API is "RESTful" when it follows these conventions.
The core idea: treat everything as a resource with a URL, and use standard HTTP methods to interact with it.
GET /users → List all users
GET /users/123 → Get user 123
POST /users → Create a new user
PUT /users/123 → Replace user 123
PATCH /users/123 → Update part of user 123
DELETE /users/123 → Delete user 123
That's it. If you understand this table, you understand 90% of REST.
HTTP Methods
| Method | Purpose | Has Body | Idempotent | Safe |
|---|---|---|---|---|
| GET | Read data | No | Yes | Yes |
| POST | Create new resource | Yes | No | No |
| PUT | Replace entire resource | Yes | Yes | No |
| PATCH | Partial update | Yes | Yes* | No |
| DELETE | Remove resource | No | Yes | No |
| HEAD | Get headers only | No | Yes | Yes |
| OPTIONS | Get allowed methods | No | Yes | Yes |
Idempotent = calling it multiple times has the same effect as calling once. Safe = doesn't modify server state.
REST Principles
1. Stateless
Every request contains all the information needed. The server doesn't remember previous requests.
// Bad: server remembers who you are
fetch("/api/my-orders");
// Good: every request identifies itself
fetch("/api/orders", {
headers: { "Authorization": "Bearer eyJhbG..." }
});
2. Resource-Based URLs
URLs represent things (nouns), not actions (verbs).
// Good - resources
GET /articles
GET /articles/42
GET /articles/42/comments
// Bad - actions in URL
GET /getArticle?id=42
POST /createArticle
GET /fetchAllArticles
3. Use HTTP Methods Correctly
// Create a new post
const response = await fetch("/api/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: "REST APIs Explained",
content: "REST is an architectural style..."
})
});
// Update an existing post
await fetch("/api/posts/42", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
title: "REST APIs Explained (Updated)",
content: "REST is an architectural style..."
})
});
// Delete a post
await fetch("/api/posts/42", {
method: "DELETE"
});
4. Proper Status Codes
Use HTTP status codes to indicate what happened:
| Code | Meaning | When to Use |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST that created something |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Invalid input from client |
| 401 | Unauthorized | Missing or invalid auth |
| 403 | Forbidden | Valid auth but not allowed |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable | Validation failed |
| 429 | Too Many Requests | Rate limited |
| 500 | Server Error | Something broke on the server |
REST vs Other Styles
| Style | Best For | Downside |
|---|---|---|
| REST | CRUD operations, simple APIs | Over-fetching, multiple roundtrips |
| GraphQL | Complex queries, mobile apps | Learning curve, caching complexity |
| gRPC | Microservices, performance-critical | Browser support, debugging |
| WebSocket | Real-time, bidirectional | Connection management |
Example: Complete CRUD Flow
const API_BASE = "https://api.example.com";
// CREATE - Post new user
async function createUser(user) {
const res = await fetch(`${API_BASE}/users`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(user)
});
if (res.status !== 201) throw new Error("Failed to create");
return res.json();
}
// READ - Get user by ID
async function getUser(id) {
const res = await fetch(`${API_BASE}/users/${id}`);
if (res.status === 404) return null;
if (!res.ok) throw new Error("Failed to fetch");
return res.json();
}
// UPDATE - Partial update
async function updateUser(id, changes) {
const res = await fetch(`${API_BASE}/users/${id}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(changes)
});
if (!res.ok) throw new Error("Failed to update");
return res.json();
}
// DELETE - Remove user
async function deleteUser(id) {
const res = await fetch(`${API_BASE}/users/${id}`, {
method: "DELETE"
});
if (res.status !== 204) throw new Error("Failed to delete");
}
Common Gotchas
PUT replaces the entire resource. If you PUT { "name": "New Name" } to a user, you'll lose all other fields. Use PATCH for partial updates.
Keep URL nesting shallow. /users/123/posts/456/comments/789/likes is a sign you need to rethink your API design. Two levels deep is usually the max.
- POST isn't idempotent - Clicking "Submit" twice creates two resources. Disable the button or use idempotency keys.
- GET requests shouldn't modify data - Never use GET to delete or update. Prefetching and caching will cause chaos.
- Versioning matters - Start with
/v1/from day one. You'll thank yourself later. - Plural nouns - Use
/users, not/user. Be consistent across your entire API. - Don't return 200 for errors - If something failed, use an error status code.
{ "status": 200, "error": "Not found" }breaks every HTTP client.
Try It
Format API Responses"REST: because someone had to standardize the 47 different ways developers were building APIs in 2000."