Building REST APIs with Node.js
Learn how to build RESTful APIs using Node.js and Express.js from scratch.
What is a REST API?
A REST (Representational State Transfer) API is an architectural style for designing networked applications. It uses HTTP requests to perform CRUD (Create, Read, Update, Delete) operations on resources.
Setting Up Your Node.js API
Initialize the Project
First, create a new directory and initialize a Node.js project:
mkdir my-api
cd my-api
npm init -yInstall Dependencies
Install Express.js and other essential packages:
npm install express cors helmet morgan dotenv
npm install -D nodemon- express: Web framework for Node.js
- cors: Enable Cross-Origin Resource Sharing
- helmet: Security middleware
- morgan: HTTP request logger
- dotenv: Environment variable management
- nodemon: Auto-restart server during development
Update package.json
Add a start script to your package.json:
{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
}Creating Your First API Server
Create a server.js file:
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet()); // Security headers
app.use(cors()); // Enable CORS
app.use(morgan('tiny')); // Request logging
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true }));
// Basic route
app.get('/', (req, res) => {
res.json({ message: 'Welcome to my API!' });
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Create a .env file for configuration:
PORT=3000
NODE_ENV=developmentBuilding RESTful Routes
Let’s create a simple users API. First, create a routes directory and add users.js:
const express = require('express');
const router = express.Router();
// Mock data (in real app, use database)
let users = [
{ id: 1, name: 'John Doe', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', email: '[email protected]' }
];
// GET all users
router.get('/', (req, res) => {
res.json(users);
});
// GET user by ID
router.get('/: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 new user
router.post('/', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
const newUser = {
id: users.length + 1,
name,
email
};
users.push(newUser);
res.status(201).json(newUser);
});
// PUT update user
router.put('/: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' });
}
const { name, email } = req.body;
if (name) user.name = name;
if (email) user.email = email;
res.json(user);
});
// DELETE user
router.delete('/:id', (req, res) => {
const userIndex = users.findIndex(u => u.id === parseInt(req.params.id));
if (userIndex === -1) {
return res.status(404).json({ error: 'User not found' });
}
users.splice(userIndex, 1);
res.status(204).send();
});
module.exports = router;Update your server.js to use the routes:
const userRoutes = require('./routes/users');
// Use user routes
app.use('/api/users', userRoutes);Error Handling
Create proper error handling middleware:
// 404 handler
app.use((req, res, next) => {
res.status(404).json({ error: 'Route not found' });
});
// Global error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});Input Validation
Add validation for your API endpoints:
// In routes/users.js
const validateUser = (req, res, next) => {
const { name, email } = req.body;
const errors = [];
if (!name || name.trim().length < 2) {
errors.push('Name must be at least 2 characters long');
}
if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
// Use validation middleware
router.post('/', validateUser, (req, res) => {
// Your existing POST logic
});Database Integration
Using MongoDB with Mongoose
Install Mongoose:
npm install mongooseCreate a database connection:
// config/database.js
const mongoose = require('mongoose');
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI);
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error('Database connection error:', error);
process.exit(1);
}
};
module.exports = connectDB;Create a User model:
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
minlength: 2
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('User', userSchema);Update your routes to use the database:
// routes/users.js
const User = require('../models/User');
// GET all users
router.get('/', async (req, res) => {
try {
const users = await User.find();
res.json(users);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch users' });
}
});
// POST new user
router.post('/', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).json(user);
} catch (error) {
res.status(400).json({ error: error.message });
}
});API Documentation
Using Swagger/OpenAPI
Install Swagger dependencies:
npm install swagger-jsdoc swagger-ui-expressAdd Swagger configuration:
// config/swagger.js
const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'My API',
version: '1.0.0',
description: 'A simple Express API',
},
servers: [
{
url: 'http://localhost:3000',
},
],
},
apis: ['./routes/*.js'],
};
const specs = swaggerJsdoc(options);
module.exports = {
serve: swaggerUi.serve,
setup: swaggerUi.setup(specs),
};Add Swagger documentation to your routes:
// routes/users.js
/**
* @swagger
* /api/users:
* get:
* summary: Get all users
* responses:
* 200:
* description: List of users
*/
router.get('/', async (req, res) => {
// Your route logic
});Testing Your API
Using Jest and Supertest
Install testing dependencies:
npm install -D jest supertestCreate a test file:
// tests/users.test.js
const request = require('supertest');
const app = require('../server');
describe('Users API', () => {
test('GET /api/users should return users list', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(Array.isArray(response.body)).toBe(true);
});
test('POST /api/users should create new user', async () => {
const newUser = {
name: 'Test User',
email: '[email protected]'
};
const response = await request(app)
.post('/api/users')
.send(newUser)
.expect(201);
expect(response.body.name).toBe(newUser.name);
expect(response.body.email).toBe(newUser.email);
});
});Production Best Practices
Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);Compression
const compression = require('compression');
app.use(compression());Environment-Specific Configuration
if (process.env.NODE_ENV === 'production') {
app.use(helmet());
app.use(morgan('combined'));
} else {
app.use(morgan('dev'));
}Testing Your API
Start your development server:
npm run devTest your endpoints using curl or Postman:
# Get all users
curl http://localhost:3000/api/users
# Create new user
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"[email protected]"}'
# Get user by ID
curl http://localhost:3000/api/users/1