REST (Representational State Transfer) is the most popular architectural style for building web APIs. This guide covers everything you need to know about RESTful APIs.
What is a REST API? #
A REST API is an application programming interface that follows REST architectural principles. It allows different software systems to communicate over HTTP using standard methods.
Key Principles of REST #
1. Stateless Communication #
Each request from client to server must contain all information needed to understand the request. The server doesn’t store client context between requests.
2. Client-Server Architecture #
The client and server are separate entities that communicate over HTTP. This separation allows them to evolve independently.
3. Uniform Interface #
REST APIs use standard HTTP methods and follow consistent naming conventions for resources.
HTTP Methods #
REST APIs use standard HTTP methods to perform operations:
GET - Retrieve Data #
// Get all users
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data));
// Get specific user
fetch('https://api.example.com/users/123')
.then(response => response.json())
.then(data => console.log(data));POST - Create Data #
fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John Doe',
email: 'john@example.com'
})
})
.then(response => response.json())
.then(data => console.log(data));PUT - Update Data (Complete Replacement) #
fetch('https://api.example.com/users/123', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'John Smith',
email: 'johnsmith@example.com'
})
})
.then(response => response.json())
.then(data => console.log(data));PATCH - Update Data (Partial Update) #
fetch('https://api.example.com/users/123', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'newemail@example.com'
})
})
.then(response => response.json())
.then(data => console.log(data));DELETE - Remove Data #
fetch('https://api.example.com/users/123', {
method: 'DELETE'
})
.then(response => {
if (response.ok) {
console.log('User deleted successfully');
}
});REST API URL Structure #
Good REST API URLs are hierarchical and represent resources:
GET /users # Get all users
GET /users/123 # Get user with ID 123
POST /users # Create new user
PUT /users/123 # Update user 123
DELETE /users/123 # Delete user 123
GET /users/123/posts # Get all posts by user 123
GET /posts/456 # Get post with ID 456
POST /users/123/posts # Create new post for user 123HTTP Status Codes #
REST APIs use standard HTTP status codes to indicate success or failure:
2xx Success
200 OK- Request succeeded201 Created- Resource created successfully204 No Content- Request succeeded, no content to return
4xx Client Errors
400 Bad Request- Invalid request format401 Unauthorized- Authentication required403 Forbidden- Authenticated but not authorized404 Not Found- Resource doesn’t exist
5xx Server Errors
500 Internal Server Error- Server encountered an error503 Service Unavailable- Server temporarily unavailable
Request and Response Format #
REST APIs typically use JSON for data exchange:
Request:
{
"name": "Jane Doe",
"email": "jane@example.com",
"role": "developer"
}Response:
{
"id": 124,
"name": "Jane Doe",
"email": "jane@example.com",
"role": "developer",
"createdAt": "2025-03-15T14:23:17Z"
}Building a Simple REST API with Express #
Here’s a basic REST API server using Node.js and Express:
const express = require('express');
const app = express();
app.use(express.json());
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// GET all users
app.get('/users', (req, res) => {
res.json(users);
});
// GET user by ID
app.get('/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
// POST create user
app.post('/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name,
email: req.body.email
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT update user
app.put('/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: 'User not found' });
user.name = req.body.name;
user.email = req.body.email;
res.json(user);
});
// DELETE user
app.delete('/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'User not found' });
users.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Best Practices #
- Use Nouns for Resources -
/usersnot/getUsers - Use Plural Names -
/usersnot/user - Use HTTP Methods Correctly - Don’t use GET for actions that modify data
- Return Appropriate Status Codes - Help clients understand the result
- Version Your API -
/v1/usersallows for future changes - Use Query Parameters for Filtering -
/users?role=admin&status=active - Implement Pagination -
/users?page=2&limit=20 - Provide Clear Error Messages - Include helpful error details in responses
Common Pitfalls to Avoid #
- Using verbs in URLs (
/createUserinstead ofPOST /users) - Not handling errors properly
- Storing state on the server
- Inconsistent naming conventions
- Not validating input data
- Missing authentication and authorization
REST APIs are the backbone of modern web development. Understanding these principles will help you build scalable and maintainable APIs.