Learn Express.js framework for building robust web applications and REST APIs with Node.js.
Learn Express.js framework for building robust web applications and REST APIs with Node.js.
Set up Express.js applications and understand the basic structure and configuration
Content by: Sachin Patel
Node.js Developer
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. It simplifies the process of building web applications and APIs by providing a clean, simple API for handling HTTP requests and responses.
// Install Express.js
npm install express
// Basic Express.js server
const express = require('express');
const app = express();
const port = 3000;
// Middleware for parsing JSON
app.use(express.json());
// Middleware for parsing URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Basic route
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Start server
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
// Express.js with ES modules
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});// Express.js core features demonstration
const express = require('express');
const app = express();
// 1. Middleware support
app.use((req, res, next) => {
console.log('Request received:', req.method, req.url);
next(); // Pass control to next middleware
});
// 2. Route parameters
app.get('/users/:id', (req, res) => {
res.json({ userId: req.params.id });
});
// 3. Query parameters
app.get('/search', (req, res) => {
res.json({ query: req.query.q, page: req.query.page });
});
// 4. Request body parsing
app.post('/users', express.json(), (req, res) => {
res.json({ message: 'User created', user: req.body });
});
// 5. Static file serving
app.use('/static', express.static('public'));
// 6. Template engine support
app.set('view engine', 'ejs');
app.get('/home', (req, res) => {
res.render('home', { title: 'Home Page' });
});
// 7. Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// 8. 404 handling
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});// Complete Express.js starter application
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const rateLimit = require('express-rate-limit');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Security middleware
app.use(helmet());
app.use(cors());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use(limiter);
// Logging middleware
app.use(morgan('combined'));
// Body parsing middleware
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Custom middleware for request timing
app.use((req, res, next) => {
req.startTime = Date.now();
next();
});
// Routes
app.get('/', (req, res) => {
res.json({
message: 'Welcome to Express.js Starter App',
version: '1.0.0',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
memory: process.memoryUsage(),
uptime: process.uptime()
});
});
// API routes
app.get('/api/users', (req, res) => {
const users = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
res.json(users);
});
app.post('/api/users', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
const newUser = {
id: Date.now(),
name,
email,
createdAt: new Date().toISOString()
};
res.status(201).json(newUser);
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(500).json({
error: 'Internal Server Error',
message: process.env.NODE_ENV === 'development' ? err.message : 'Something went wrong'
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
process.exit(0);
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully');
process.exit(0);
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});
module.exports = app;Test your understanding of this topic:
Learn Express.js routing patterns and middleware implementation for request processing
Content by: Parth Patel
Node.js Developer
Routing refers to determining how an application responds to a client request to a particular endpoint. Express.js provides a robust routing system that allows you to define routes for different HTTP methods and URL patterns.
const express = require('express');
const app = express();
// GET route
app.get('/users', (req, res) => {
res.json({ message: 'Get all users' });
});
// POST route
app.post('/users', (req, res) => {
res.json({ message: 'Create new user', data: req.body });
});
// PUT route
app.put('/users/:id', (req, res) => {
res.json({ message: 'Update user', id: req.params.id });
});
// DELETE route
app.delete('/users/:id', (req, res) => {
res.json({ message: 'Delete user', id: req.params.id });
});
// Route with parameters
app.get('/users/:id', (req, res) => {
res.json({ message: 'Get user by ID', id: req.params.id });
});
// Route with query parameters
app.get('/search', (req, res) => {
const { q, page } = req.query;
res.json({ message: 'Search results', query: q, page: page });
});
// Multiple route handlers
app.get('/users/:id',
(req, res, next) => {
console.log('Middleware 1');
next();
},
(req, res, next) => {
console.log('Middleware 2');
next();
},
(req, res) => {
res.json({ message: 'Get user', id: req.params.id });
}
);const express = require('express');
const app = express();
// Application-level middleware
app.use((req, res, next) => {
console.log('Request URL:', req.url);
console.log('Request Method:', req.method);
console.log('Timestamp:', new Date().toISOString());
next();
});
// Middleware for specific routes
app.use('/api', (req, res, next) => {
console.log('API request');
next();
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// Custom middleware function
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// Verify token logic here
req.user = { id: 1, name: 'John Doe' };
next();
};
// Using custom middleware
app.get('/protected', authMiddleware, (req, res) => {
res.json({ message: 'Protected route', user: req.user });
});
// Middleware for parsing JSON
app.use(express.json());
// Middleware for serving static files
app.use(express.static('public'));
// CORS middleware
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
next();
});// Advanced routing patterns and techniques
const express = require('express');
const router = express.Router();
// Route chaining
router.route('/users')
.get((req, res) => {
res.json({ message: 'Get all users' });
})
.post((req, res) => {
res.json({ message: 'Create user', data: req.body });
});
// Route with multiple parameters
router.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
// Route with optional parameters
router.get('/users/:id?', (req, res) => {
if (req.params.id) {
res.json({ message: 'Get user by ID', id: req.params.id });
} else {
res.json({ message: 'Get all users' });
}
});
// Route with regex patterns
router.get('/users/:id(\d+)', (req, res) => {
res.json({ message: 'User ID must be numeric', id: req.params.id });
});
// Route with wildcards
router.get('/files/*', (req, res) => {
res.json({ message: 'File path', path: req.params[0] });
});
// Route with multiple callbacks
router.get('/admin',
(req, res, next) => {
console.log('First callback');
next();
},
(req, res, next) => {
console.log('Second callback');
next();
},
(req, res) => {
res.json({ message: 'Admin route' });
}
);
module.exports = router;// Custom middleware suite for Express.js application
const express = require('express');
const app = express();
// 1. Request ID Middleware
const requestId = (req, res, next) => {
req.id = Math.random().toString(36).substr(2, 9);
res.setHeader('X-Request-ID', req.id);
next();
};
// 2. Request Logger Middleware
const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`[${req.id}] ${req.method} ${req.url} - ${res.statusCode} (${duration}ms)`);
});
next();
};
// 3. Authentication Middleware
const authenticate = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
// Simple token validation (in real app, use JWT)
if (token === 'Bearer valid-token') {
req.user = { id: 1, name: 'John Doe' };
next();
} else {
res.status(401).json({ error: 'Invalid token' });
}
};
// 4. Rate Limiting Middleware
const rateLimit = (maxRequests = 100, windowMs = 15 * 60 * 1000) => {
const requests = new Map();
return (req, res, next) => {
const ip = req.ip;
const now = Date.now();
const windowStart = now - windowMs;
// Clean old entries
for (const [key, timestamp] of requests.entries()) {
if (timestamp < windowStart) {
requests.delete(key);
}
}
// Check current IP
const ipRequests = Array.from(requests.entries())
.filter(([key, timestamp]) => key.startsWith(ip) && timestamp > windowStart);
if (ipRequests.length >= maxRequests) {
return res.status(429).json({ error: 'Too many requests' });
}
requests.set(`${ip}-${now}`, now);
next();
};
};
// 5. Validation Middleware
const validateUser = (req, res, next) => {
const { name, email, age } = req.body;
const errors = [];
if (!name || name.length < 2) {
errors.push('Name must be at least 2 characters');
}
if (!email || !email.includes('@')) {
errors.push('Valid email is required');
}
if (!age || age < 0 || age > 150) {
errors.push('Age must be between 0 and 150');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
next();
};
// 6. Response Time Middleware
const responseTime = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
res.setHeader('X-Response-Time', `${duration}ms`);
});
next();
};
// 7. Error Handler Middleware
const errorHandler = (err, req, res, next) => {
console.error(`[${req.id}] Error:`, err);
if (err.name === 'ValidationError') {
return res.status(400).json({ error: 'Validation failed', details: err.message });
}
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: 'Unauthorized' });
}
res.status(500).json({ error: 'Internal server error' });
};
// Apply middleware
app.use(requestId);
app.use(requestLogger);
app.use(responseTime);
app.use(express.json());
app.use(rateLimit(10, 60000)); // 10 requests per minute
// Routes
app.get('/', (req, res) => {
res.json({ message: 'Hello World', requestId: req.id });
});
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'Protected route', user: req.user });
});
app.post('/users', validateUser, (req, res) => {
res.json({ message: 'User created', user: req.body });
});
// Error handling
app.use(errorHandler);
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
module.exports = app;Test your understanding of this topic:
Master request and response handling in Express.js applications
Content by: Bansi Patel
Node.js Developer
The request object (req) contains information about the HTTP request that triggered the route handler. It includes properties like headers, body, parameters, and query strings.
const express = require('express');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Accessing request properties
app.get('/example', (req, res) => {
// URL and path
console.log('URL:', req.url);
console.log('Path:', req.path);
console.log('Base URL:', req.baseUrl);
// HTTP method
console.log('Method:', req.method);
// Headers
console.log('Headers:', req.headers);
console.log('User Agent:', req.get('User-Agent'));
// Query parameters
console.log('Query:', req.query);
console.log('Search:', req.query.search);
// Route parameters
console.log('Params:', req.params);
// Request body (for POST, PUT, PATCH)
console.log('Body:', req.body);
// IP address
console.log('IP:', req.ip);
// Hostname
console.log('Hostname:', req.hostname);
res.json({ message: 'Request details logged' });
});
// Handling different content types
app.post('/users', (req, res) => {
const { name, email, age } = req.body;
// Validate required fields
if (!name || !email) {
return res.status(400).json({ error: 'Name and email are required' });
}
// Process the data
const user = { name, email, age: age || null };
res.status(201).json({ message: 'User created', user });
});
// File upload handling
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
res.json({
message: 'File uploaded successfully',
filename: req.file.filename,
originalname: req.file.originalname,
size: req.file.size
});
});const express = require('express');
const app = express();
// Sending different response types
app.get('/responses', (req, res) => {
// JSON response
res.json({ message: 'JSON response' });
// HTML response
res.send('<h1>HTML response</h1>');
// Plain text response
res.send('Plain text response');
// Redirect
res.redirect('/new-page');
// Send file
res.sendFile('/path/to/file.html');
// Download file
res.download('/path/to/file.pdf');
});
// Setting response headers
app.get('/headers', (req, res) => {
res.set('Content-Type', 'application/json');
res.set('X-Custom-Header', 'Custom Value');
res.status(200).json({ message: 'Headers set' });
});
// Different HTTP status codes
app.get('/status', (req, res) => {
// Success responses
res.status(200).json({ message: 'OK' });
res.status(201).json({ message: 'Created' });
// Client error responses
res.status(400).json({ error: 'Bad Request' });
res.status(401).json({ error: 'Unauthorized' });
res.status(404).json({ error: 'Not Found' });
// Server error responses
res.status(500).json({ error: 'Internal Server Error' });
res.status(503).json({ error: 'Service Unavailable' });
});
// Streaming responses
app.get('/stream', (req, res) => {
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Transfer-Encoding', 'chunked');
let counter = 0;
const interval = setInterval(() => {
res.write(`Data chunk ${counter}\n`);
counter++;
if (counter >= 10) {
clearInterval(interval);
res.end('Stream complete');
}
}, 1000);
});
// Error responses
app.get('/error', (req, res) => {
try {
// Simulate an error
throw new Error('Something went wrong');
} catch (error) {
res.status(500).json({
error: 'Internal Server Error',
message: error.message
});
}
});// Advanced request/response handling patterns
const express = require('express');
const app = express();
// Request streaming for large data
app.get('/stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/plain',
'Transfer-Encoding': 'chunked'
});
let counter = 0;
const interval = setInterval(() => {
res.write(`Chunk ${counter}\n`);
counter++;
if (counter >= 10) {
clearInterval(interval);
res.end('Stream complete');
}
}, 1000);
});
// Response compression
const compression = require('compression');
app.use(compression());
// Custom response methods
app.use((req, res, next) => {
res.success = (data, message = 'Success') => {
res.json({ success: true, message, data });
};
res.error = (message, statusCode = 400) => {
res.status(statusCode).json({ success: false, message });
};
res.paginated = (data, page, limit, total) => {
res.json({
data,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
};
next();
});
// Usage of custom response methods
app.get('/api/users', (req, res) => {
const users = [{ id: 1, name: 'John' }, { id: 2, name: 'Jane' }];
res.success(users, 'Users retrieved successfully');
});
app.get('/api/posts', (req, res) => {
const { page = 1, limit = 10 } = req.query;
const posts = Array.from({ length: 50 }, (_, i) => ({ id: i + 1, title: `Post ${i + 1}` }));
const start = (page - 1) * limit;
const end = start + parseInt(limit);
const paginatedPosts = posts.slice(start, end);
res.paginated(paginatedPosts, page, limit, posts.length);
});// Complete RESTful API with advanced request/response handling
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const rateLimit = require('express-rate-limit');
const { body, validationResult } = require('express-validator');
const app = express();
// Security and performance middleware
app.use(helmet());
app.use(cors());
app.use(compression());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);
// Custom middleware for request processing
app.use((req, res, next) => {
req.startTime = Date.now();
req.requestId = Math.random().toString(36).substr(2, 9);
// Custom response methods
res.success = (data, message = 'Success', statusCode = 200) => {
res.status(statusCode).json({
success: true,
message,
data,
requestId: req.requestId,
timestamp: new Date().toISOString()
});
};
res.error = (message, statusCode = 400, details = null) => {
res.status(statusCode).json({
success: false,
message,
details,
requestId: req.requestId,
timestamp: new Date().toISOString()
});
};
res.paginated = (data, page, limit, total, message = 'Data retrieved successfully') => {
res.json({
success: true,
message,
data,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit),
hasNext: page * limit < total,
hasPrev: page > 1
},
requestId: req.requestId,
timestamp: new Date().toISOString()
});
};
next();
});
// In-memory data store (in real app, use database)
let users = [
{ id: 1, name: 'John Doe', email: 'john@example.com', age: 30, createdAt: new Date() },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 25, createdAt: new Date() }
];
let nextId = 3;
// Validation middleware
const validateUser = [
body('name').isLength({ min: 2 }).withMessage('Name must be at least 2 characters'),
body('email').isEmail().withMessage('Must be a valid email'),
body('age').isInt({ min: 0, max: 150 }).withMessage('Age must be between 0 and 150'),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.error('Validation failed', 400, errors.array());
}
next();
}
];
// Routes
app.get('/', (req, res) => {
res.success({
message: 'Welcome to Advanced REST API',
version: '1.0.0',
endpoints: {
users: '/api/users',
health: '/api/health'
}
});
});
app.get('/api/health', (req, res) => {
res.success({
status: 'healthy',
uptime: process.uptime(),
memory: process.memoryUsage(),
timestamp: new Date().toISOString()
});
});
// GET /api/users - Get all users with pagination
app.get('/api/users', (req, res) => {
const { page = 1, limit = 10, search, sortBy = 'id', order = 'asc' } = req.query;
let filteredUsers = users;
// Search functionality
if (search) {
filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(search.toLowerCase()) ||
user.email.toLowerCase().includes(search.toLowerCase())
);
}
// Sorting
filteredUsers.sort((a, b) => {
const aVal = a[sortBy];
const bVal = b[sortBy];
if (order === 'desc') {
return bVal > aVal ? 1 : -1;
}
return aVal > bVal ? 1 : -1;
});
// Pagination
const start = (page - 1) * limit;
const end = start + parseInt(limit);
const paginatedUsers = filteredUsers.slice(start, end);
res.paginated(paginatedUsers, page, limit, filteredUsers.length);
});
// GET /api/users/:id - Get user by ID
app.get('/api/users/:id', (req, res) => {
const { id } = req.params;
const user = users.find(u => u.id === parseInt(id));
if (!user) {
return res.error('User not found', 404);
}
res.success(user);
});
// POST /api/users - Create new user
app.post('/api/users', validateUser, (req, res) => {
const { name, email, age } = req.body;
// Check if email already exists
const existingUser = users.find(u => u.email === email);
if (existingUser) {
return res.error('Email already exists', 409);
}
const newUser = {
id: nextId++,
name,
email,
age: parseInt(age),
createdAt: new Date()
};
users.push(newUser);
res.success(newUser, 'User created successfully', 201);
});
// PUT /api/users/:id - Update user
app.put('/api/users/:id', validateUser, (req, res) => {
const { id } = req.params;
const { name, email, age } = req.body;
const userIndex = users.findIndex(u => u.id === parseInt(id));
if (userIndex === -1) {
return res.error('User not found', 404);
}
// Check if email already exists (excluding current user)
const existingUser = users.find(u => u.email === email && u.id !== parseInt(id));
if (existingUser) {
return res.error('Email already exists', 409);
}
users[userIndex] = {
...users[userIndex],
name,
email,
age: parseInt(age),
updatedAt: new Date()
};
res.success(users[userIndex], 'User updated successfully');
});
// DELETE /api/users/:id - Delete user
app.delete('/api/users/:id', (req, res) => {
const { id } = req.params;
const userIndex = users.findIndex(u => u.id === parseInt(id));
if (userIndex === -1) {
return res.error('User not found', 404);
}
const deletedUser = users.splice(userIndex, 1)[0];
res.success(deletedUser, 'User deleted successfully');
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(`[${req.requestId}] Error:`, err);
if (err.type === 'entity.parse.failed') {
return res.error('Invalid JSON in request body', 400);
}
res.error('Internal server error', 500);
});
// 404 handler
app.use((req, res) => {
res.error('Route not found', 404);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
console.log(`API Documentation: http://localhost:${PORT}`);
});
module.exports = app;Test your understanding of this topic:
Implement proper error handling and validation in Express.js applications
Content by: Noman Mansuri
Node.js Developer
Error handling is crucial for building robust Express.js applications. Express.js provides built-in error handling mechanisms and allows you to create custom error handlers for different scenarios.
const express = require('express');
const app = express();
// Error handling middleware (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
// Async error handling
app.get('/async', async (req, res, next) => {
try {
const result = await someAsyncOperation();
res.json(result);
} catch (error) {
next(error); // Pass error to error handling middleware
}
});
// Express 5.0+ async error handling
app.get('/async-v5', async (req, res) => {
const result = await someAsyncOperation();
res.json(result);
// Errors are automatically caught and passed to error middleware
});
// Custom error class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
// Using custom error class
app.get('/users/:id', (req, res, next) => {
const userId = req.params.id;
if (!userId || isNaN(userId)) {
return next(new AppError('Invalid user ID', 400));
}
// Process user logic
res.json({ message: 'User found', id: userId });
});
// Error handling middleware for custom errors
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'development') {
res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
} else {
// Production error response
if (err.isOperational) {
res.status(err.statusCode).json({
status: err.status,
message: err.message
});
} else {
// Programming or unknown errors
console.error('ERROR 💥', err);
res.status(500).json({
status: 'error',
message: 'Something went wrong!'
});
}
}
});const express = require('express');
const app = express();
app.use(express.json());
// Manual validation
app.post('/users', (req, res, next) => {
const { name, email, age } = req.body;
// Validation
if (!name || name.trim().length < 2) {
return res.status(400).json({ error: 'Name must be at least 2 characters long' });
}
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Valid email is required' });
}
if (age && (age < 0 || age > 120)) {
return res.status(400).json({ error: 'Age must be between 0 and 120' });
}
// Process valid data
res.status(201).json({ message: 'User created successfully' });
});
// Using validation libraries
const { body, validationResult } = require('express-validator');
app.post('/users-validated', [
body('name').isLength({ min: 2 }).withMessage('Name must be at least 2 characters'),
body('email').isEmail().withMessage('Must be a valid email'),
body('age').optional().isInt({ min: 0, max: 120 }).withMessage('Age must be between 0 and 120')
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
res.status(201).json({ message: 'User created successfully' });
});
// Sanitization
const { body, sanitizeBody } = require('express-validator');
app.post('/users-sanitized', [
body('name').trim().escape(),
body('email').normalizeEmail(),
sanitizeBody('age').toInt()
], (req, res) => {
// Data is now sanitized
res.status(201).json({ message: 'User created', data: req.body });
});// Advanced error handling patterns for Express.js
const express = require('express');
const app = express();
// Custom error classes
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, details = []) {
super(message, 400);
this.details = details;
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
}
}
// Error handling middleware
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log error
console.error('Error:', err);
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new AppError(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new AppError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(val => val.message).join(', ');
error = new ValidationError(message);
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
const message = 'Invalid token';
error = new AppError(message, 401);
}
if (err.name === 'TokenExpiredError') {
const message = 'Token expired';
error = new AppError(message, 401);
}
res.status(error.statusCode || 500).json({
success: false,
error: error.message || 'Server Error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
...(error.details && { details: error.details })
});
};
// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Usage examples
app.get('/users/:id', asyncHandler(async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
return next(new NotFoundError('User'));
}
res.json({ success: true, data: user });
}));
app.post('/users', asyncHandler(async (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
return next(new ValidationError('Name and email are required'));
}
const user = await User.create({ name, email });
res.status(201).json({ success: true, data: user });
}));
// Global error handler
app.use(errorHandler);
module.exports = app;// Complete error handling system for Express.js application
const express = require('express');
const winston = require('winston');
const Sentry = require('@sentry/node');
const app = express();
// Configure logging
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
new winston.transports.Console({
format: winston.format.simple()
})
]
});
// Initialize Sentry for error monitoring
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || 'development'
});
// Custom error classes
class AppError extends Error {
constructor(message, statusCode, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
this.timestamp = new Date().toISOString();
Error.captureStackTrace(this, this.constructor);
}
}
class ValidationError extends AppError {
constructor(message, field = null) {
super(message, 400);
this.field = field;
this.type = 'ValidationError';
}
}
class AuthenticationError extends AppError {
constructor(message = 'Authentication failed') {
super(message, 401);
this.type = 'AuthenticationError';
}
}
class AuthorizationError extends AppError {
constructor(message = 'Insufficient permissions') {
super(message, 403);
this.type = 'AuthorizationError';
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
this.type = 'NotFoundError';
}
}
class ConflictError extends AppError {
constructor(message = 'Resource conflict') {
super(message, 409);
this.type = 'ConflictError';
}
}
class RateLimitError extends AppError {
constructor(message = 'Too many requests') {
super(message, 429);
this.type = 'RateLimitError';
}
}
// Error handling utilities
const handleAsync = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
const handleValidation = (schema) => (req, res, next) => {
const { error } = schema.validate(req.body);
if (error) {
const details = error.details.map(detail => ({
field: detail.path.join('.'),
message: detail.message
}));
return next(new ValidationError('Validation failed', details));
}
next();
};
// Error response formatter
const formatErrorResponse = (err, req) => {
const response = {
success: false,
error: {
message: err.message,
type: err.type || 'UnknownError',
timestamp: err.timestamp || new Date().toISOString(),
requestId: req.requestId || 'unknown'
}
};
// Add additional details in development
if (process.env.NODE_ENV === 'development') {
response.error.stack = err.stack;
response.error.details = err.details;
}
return response;
};
// Global error handler
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
// Log error
logger.error({
message: err.message,
stack: err.stack,
requestId: req.requestId,
url: req.url,
method: req.method,
ip: req.ip,
userAgent: req.get('User-Agent')
});
// Send to Sentry
Sentry.captureException(err);
// Handle specific error types
if (err.name === 'CastError') {
error = new NotFoundError('Resource');
}
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
error = new ConflictError(`${field} already exists`);
}
if (err.name === 'ValidationError') {
const details = Object.values(err.errors).map(val => ({
field: val.path,
message: val.message
}));
error = new ValidationError('Validation failed', details);
}
if (err.name === 'JsonWebTokenError') {
error = new AuthenticationError('Invalid token');
}
if (err.name === 'TokenExpiredError') {
error = new AuthenticationError('Token expired');
}
// Send error response
const statusCode = error.statusCode || 500;
const response = formatErrorResponse(error, req);
res.status(statusCode).json(response);
};
// 404 handler
const notFoundHandler = (req, res, next) => {
const error = new NotFoundError(`Route ${req.originalUrl}`);
next(error);
};
// Rate limiting error handler
const rateLimitHandler = (req, res) => {
const error = new RateLimitError('Too many requests, please try again later');
const response = formatErrorResponse(error, req);
res.status(429).json(response);
};
// Apply middleware
app.use(Sentry.requestHandler());
app.use(express.json());
// Request ID middleware
app.use((req, res, next) => {
req.requestId = Math.random().toString(36).substr(2, 9);
res.setHeader('X-Request-ID', req.requestId);
next();
});
// Example routes with error handling
app.get('/users/:id', handleAsync(async (req, res, next) => {
const { id } = req.params;
if (!id || isNaN(id)) {
throw new ValidationError('Invalid user ID');
}
// Simulate database operation
const user = await simulateDatabaseOperation('findUser', id);
if (!user) {
throw new NotFoundError('User');
}
res.json({ success: true, data: user });
}));
app.post('/users', handleValidation(userSchema), handleAsync(async (req, res, next) => {
const { name, email } = req.body;
// Simulate database operation
const existingUser = await simulateDatabaseOperation('findUserByEmail', email);
if (existingUser) {
throw new ConflictError('User with this email already exists');
}
const user = await simulateDatabaseOperation('createUser', { name, email });
res.status(201).json({ success: true, data: user });
}));
// Simulate database operations
async function simulateDatabaseOperation(operation, data) {
// Simulate async operation
await new Promise(resolve => setTimeout(resolve, 100));
switch (operation) {
case 'findUser':
return data === '1' ? { id: 1, name: 'John Doe', email: 'john@example.com' } : null;
case 'findUserByEmail':
return data === 'john@example.com' ? { id: 1, name: 'John Doe', email: 'john@example.com' } : null;
case 'createUser':
return { id: Date.now(), ...data, createdAt: new Date() };
default:
return null;
}
}
// User validation schema (using Joi)
const Joi = require('joi');
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0).max(150).optional()
});
// Apply error handlers
app.use(notFoundHandler);
app.use(errorHandler);
app.use(Sentry.errorHandler());
// Graceful shutdown
process.on('SIGTERM', () => {
logger.info('SIGTERM received, shutting down gracefully');
process.exit(0);
});
process.on('SIGINT', () => {
logger.info('SIGINT received, shutting down gracefully');
process.exit(0);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info(`Server running on port ${PORT}`);
});
module.exports = app;Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 3