Building RESTful APIs with Node.js
RESTful APIs are the backbone of modern web applications. They allow your frontend to communicate with your backend, enable mobile apps to fetch data, and make it possible for different services to work together. Let’s build one with Node.js and Express.
What is a REST API?
REST (Representational State Transfer) is an architectural style for designing networked applications. A RESTful API uses HTTP requests to perform CRUD operations:
- GET: Retrieve data
- POST: Create new data
- PUT/PATCH: Update existing data
- DELETE: Remove data
Setting Up Your Project
First, create a new Node.js project:
mkdir my-api
cd my-api
npm init -y
npm install express
Creating Your First Endpoint
Create server.js:
const express = require('express');
const app = express();
const PORT = 3000;
// Middleware to parse JSON
app.use(express.json());
// Sample data
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// GET all users
app.get('/api/users', (req, res) => {
res.json(users);
});
// GET single user
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ message: 'User not found' });
res.json(user);
});
// POST create user
app.post('/api/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('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ message: 'User not found' });
user.name = req.body.name;
user.email = req.body.email;
res.json(user);
});
// DELETE user
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ message: 'User not found' });
users.splice(index, 1);
res.status(204).send();
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
REST API Best Practices
1. Use Nouns, Not Verbs
✅ Good: /api/users
❌ Bad: /api/getUsers
The HTTP method already indicates the action.
2. Use Plural Nouns
✅ Good: /api/users/123
❌ Bad: /api/user/123
3. Use Proper HTTP Status Codes
200 OK: Successful GET, PUT, PATCH201 Created: Successful POST204 No Content: Successful DELETE400 Bad Request: Invalid input404 Not Found: Resource doesn’t exist500 Internal Server Error: Server error
4. Version Your API
app.get('/api/v1/users', (req, res) => {
// Version 1 implementation
});
5. Implement Filtering and Pagination
app.get('/api/users', (req, res) => {
let result = users;
// Filter by name
if (req.query.name) {
result = result.filter(u =>
u.name.toLowerCase().includes(req.query.name.toLowerCase())
);
}
// Pagination
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
const paginatedResults = result.slice(startIndex, endIndex);
res.json({
data: paginatedResults,
page,
totalPages: Math.ceil(result.length / limit)
});
});
Adding Middleware
Middleware functions can modify requests before they reach your route handlers:
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next();
});
// Authentication middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ message: 'Unauthorized' });
// Verify token...
next();
};
app.get('/api/protected', authenticate, (req, res) => {
res.json({ message: 'This is protected' });
});
Error Handling
Implement centralized error handling:
// Error handling middleware (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
message: 'Something went wrong!',
error: process.env.NODE_ENV === 'development' ? err.message : {}
});
});
Connecting to a Database
For production, replace the in-memory array with a real database:
// Example with MongoDB and Mongoose
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: String,
email: String
});
const User = mongoose.model('User', UserSchema);
app.get('/api/users', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ message: error.message });
}
});
Testing Your API
Use tools like:
- Postman: GUI for testing APIs
- curl: Command-line testing
- Jest + Supertest: Automated testing
Example curl command:
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'
Next Steps
- Add input validation (try
express-validator) - Implement authentication (JWT, OAuth)
- Add rate limiting to prevent abuse
- Document your API (try Swagger/OpenAPI)
- Deploy to production (Heroku, AWS, DigitalOcean)
Building RESTful APIs with Node.js is straightforward once you understand the patterns. Start simple, follow best practices, and scale as needed!