380 words
2 minutes
REST API Design Best Practices

REST API Design Best Practices#

After building 50+ APIs, here’s how I design them.

Basic Principles#

1. Use Nouns, Not Verbs#

Good:
GET /users
GET /users/123
POST /users
PUT /users/123
DELETE /users/123
Bad:
GET /getUsers
POST /createUser
DELETE /deleteUser/123

2. Use Proper HTTP Methods#

MethodPurposeExample
GETReadGet users
POSTCreateAdd new user
PUTUpdate (full)Replace user
PATCHUpdate (partial)Update email only
DELETERemoveDelete user

3. Use Status Codes#

// Success
200 OK // Request succeeded
201 Created // Resource created
204 No Content // Successful, no body
// Client Errors
400 Bad Request // Invalid input
401 Unauthorized // Not authenticated
403 Forbidden // Not authorized
404 Not Found // Resource doesn't exist
422 Unprocessable // Validation error
// Server Errors
500 Internal Error // Something broke

Example API (Express.js)#

const express = require('express');
const app = express();
// GET all users
app.get('/api/users', async (req, res) => {
try {
const users = await User.find();
res.json({
success: true,
count: users.length,
data: users
});
} catch (err) {
res.status(500).json({
success: false,
error: 'Server Error'
});
}
});
// GET single user
app.get('/api/users/:id', async (req, res) => {
try {
const user = await User.findById(req.params.id);
if (!user) {
return res.status(404).json({
success: false,
error: 'User not found'
});
}
res.json({ success: true, data: user });
} catch (err) {
res.status(500).json({ success: false, error: 'Server Error' });
}
});
// POST create user
app.post('/api/users', async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201).json({
success: true,
data: user
});
} catch (err) {
res.status(400).json({
success: false,
error: err.message
});
}
});

Response Format#

Always consistent structure:

{
"success": true,
"count": 2,
"data": [
{ "id": 1, "name": "User 1" },
{ "id": 2, "name": "User 2" }
]
}

Error response:

{
"success": false,
"error": "User not found"
}

Pagination#

GET /api/users?page=1&limit=10
Response:
{
"success": true,
"count": 10,
"total": 100,
"page": 1,
"pages": 10,
"data": [...]
}

Filtering & Sorting#

GET /api/products?category=electronics&price[lte]=1000
GET /api/posts?sort=-createdAt
GET /api/users?fields=name,email

Authentication#

// Middleware
const protect = async (req, res, next) => {
let token;
if (req.headers.authorization?.startsWith('Bearer')) {
token = req.headers.authorization.split(' ')[1];
}
if (!token) {
return res.status(401).json({
success: false,
error: 'Not authorized'
});
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id);
next();
} catch (err) {
res.status(401).json({ success: false, error: 'Not authorized' });
}
};
// Protected route
app.get('/api/profile', protect, getProfile);

API Versioning#

/api/v1/users
/api/v2/users

Documentation#

Use Swagger/OpenAPI:

paths:
/users:
get:
summary: Get all users
responses:
200:
description: Success

Good APIs are intuitive. Make them easy to use!

REST API Design Best Practices
https://blog.lukkid.dev/posts/rest-api-design/
Author
LUKKID
Published at
2024-05-15
License
CC BY-NC-SA 4.0