
Expressjs Development
- 399 installs
- 61 repo stars
- Updated June 13, 2026
- manutej/luxor-claude-marketplace
expressjs-development is a Claude marketplace skill that scaffolds and extends Express.js HTTP APIs with routing, validation, middleware, and production-ready server structure for backend developers.
About
expressjs-development is a skill from the manutej/luxor-claude-marketplace collection for building and extending Express.js HTTP services. It guides agents through defining routes, wiring middleware, implementing validation and centralized error handling, and organizing servers for production deployment. Developers reach for expressjs-development when standing up REST or JSON APIs, refactoring middleware stacks, or hardening Node.js services before release. The skill fits agent-assisted backend work where conventions for folder layout, error boundaries, and request lifecycle management must be applied consistently across new endpoints.
- Express routing and middleware chains
- Request validation and error handling
- REST API structure conventions
- Security headers and auth hooks
- Local run and test workflows
Expressjs Development by the numbers
- 399 all-time installs (skills.sh)
- +21 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,083 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill expressjs-developmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 399 |
|---|---|
| repo stars | ★ 61 |
| Last updated | June 13, 2026 |
| Repository | manutej/luxor-claude-marketplace ↗ |
How do you structure production Express.js APIs?
Build and extend Express.js HTTP APIs and middleware with routing, validation, error handling, and production-ready server structure.
Who is it for?
Node.js backend developers creating or refactoring Express.js REST APIs with consistent middleware and error-handling patterns.
Skip if: Teams standardized on Fastify, NestJS, or non-Node frameworks who will not adopt Express.js patterns.
When should I use this skill?
A Node.js service needs new Express routes, middleware, validation, or production server structure.
What you get
Express route modules, middleware stacks, validation layers, error handlers, and organized server entrypoints.
- Express route modules
- middleware stacks
- error-handling layers
Files
Express.js Development Skill
This skill provides comprehensive guidance for building production-ready web applications and REST APIs using Express.js, covering routing, middleware, request/response handling, error handling, authentication, validation, and deployment best practices.
When to Use This Skill
Use this skill when:
- Building RESTful APIs for web and mobile applications
- Creating backend services and microservices
- Developing web servers with server-side rendering
- Implementing API gateways and proxy servers
- Building real-time applications with WebSocket support
- Creating middleware-based request processing pipelines
- Developing authentication and authorization systems
- Implementing file upload and download services
- Building webhook handlers and integrations
- Creating serverless functions with Express
Core Concepts
Application Setup
Express applications are built by creating an instance of Express and configuring middleware and routes.
Basic Express Application:
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.get('/', (req, res) => {
res.send('Hello World!');
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Application with Configuration:
const express = require('express');
const app = express();
// App settings
app.set('port', process.env.PORT || 3000);
app.set('env', process.env.NODE_ENV || 'development');
app.set('trust proxy', 1); // Trust first proxy
// View engine setup (optional)
app.set('view engine', 'ejs');
app.set('views', './views');
// Static files
app.use(express.static('public'));
// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
module.exports = app;Routing
Routing refers to how an application's endpoints (URIs) respond to client requests.
Basic Routes:
const express = require('express');
const app = express();
// HTTP Methods
app.get('/users', (req, res) => {
res.json({ message: 'Get all users' });
});
app.post('/users', (req, res) => {
res.json({ message: 'Create user' });
});
app.put('/users/:id', (req, res) => {
res.json({ message: `Update user ${req.params.id}` });
});
app.delete('/users/:id', (req, res) => {
res.json({ message: `Delete user ${req.params.id}` });
});
// Multiple methods on same route
app.route('/users/:id')
.get((req, res) => res.json({ message: 'Get user' }))
.put((req, res) => res.json({ message: 'Update user' }))
.delete((req, res) => res.json({ message: 'Delete user' }));Route Parameters:
// Single parameter
app.get('/users/:userId', (req, res) => {
const { userId } = req.params;
res.json({ userId });
});
// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
// Optional parameters with regex
app.get('/users/:userId/posts/:postId?', (req, res) => {
// postId is optional
res.json(req.params);
});
// Parameter validation
app.param('userId', (req, res, next, id) => {
// Validate or transform parameter
if (!id.match(/^\d+$/)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
req.userId = parseInt(id);
next();
});Query Strings:
// GET /search?q=express&limit=10&page=2
app.get('/search', (req, res) => {
const { q, limit = 20, page = 1 } = req.query;
res.json({
query: q,
limit: parseInt(limit),
page: parseInt(page)
});
});Router Modules:
// routes/users.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({ message: 'Get all users' });
});
router.get('/:id', (req, res) => {
res.json({ message: `Get user ${req.params.id}` });
});
router.post('/', (req, res) => {
res.json({ message: 'Create user' });
});
module.exports = router;
// app.js
const usersRouter = require('./routes/users');
app.use('/api/users', usersRouter);Middleware
Middleware functions have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle.
Application-Level Middleware:
// Executed for every request
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
// Executed for specific path
app.use('/api', (req, res, next) => {
req.startTime = Date.now();
next();
});
// Multiple middleware functions
app.use(
express.json(),
express.urlencoded({ extended: true }),
cookieParser()
);Router-Level Middleware:
const router = express.Router();
// Middleware for all routes in this router
router.use((req, res, next) => {
console.log('Router middleware');
next();
});
// Middleware for specific route
router.get('/users',
authMiddleware,
validationMiddleware,
(req, res) => {
res.json({ users: [] });
}
);Built-in Middleware:
// Parse JSON bodies
app.use(express.json());
// Parse URL-encoded bodies
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));Third-Party Middleware:
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const compression = require('compression');
// Security headers
app.use(helmet());
// CORS
app.use(cors({
origin: 'https://example.com',
credentials: true
}));
// Logging
app.use(morgan('combined'));
// Compression
app.use(compression());Custom Middleware:
// Request logging middleware
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.path} ${res.statusCode} ${duration}ms`);
});
next();
}
// Authentication middleware
function requireAuth(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
// Request validation middleware
function validateUser(req, res, next) {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({
error: 'Email and password are required'
});
}
if (!email.includes('@')) {
return res.status(400).json({ error: 'Invalid email' });
}
next();
}
app.use(requestLogger);
app.post('/login', validateUser, loginHandler);
app.get('/protected', requireAuth, protectedHandler);Request Object
The request object represents the HTTP request and has properties for query strings, parameters, body, headers, etc.
Request Properties:
app.post('/api/users/:id', (req, res) => {
// Route parameters
const { id } = req.params;
// Query string
const { sort, filter } = req.query;
// Request body
const { name, email } = req.body;
// Headers
const userAgent = req.get('User-Agent');
const contentType = req.get('Content-Type');
// Request info
const method = req.method;
const path = req.path;
const url = req.url;
const baseUrl = req.baseUrl;
const protocol = req.protocol;
const hostname = req.hostname;
const ip = req.ip;
// Cookies (requires cookie-parser)
const { sessionId } = req.cookies;
res.json({ id, name, email });
});Request Methods:
app.post('/upload', (req, res) => {
// Check content type
if (req.is('application/json')) {
// Handle JSON
}
// Check accept header
if (req.accepts('json')) {
res.json({ data: 'json response' });
} else if (req.accepts('html')) {
res.send('<html>html response</html>');
}
// Get header value
const auth = req.get('Authorization');
// Get range header
const range = req.range(1000);
});Response Object
The response object represents the HTTP response that an Express app sends when it gets an HTTP request.
Sending Responses:
app.get('/api/data', (req, res) => {
// Send JSON
res.json({ message: 'Success', data: [] });
// Send string
res.send('Hello World');
// Send status
res.sendStatus(200); // Equivalent to res.status(200).send('OK')
// Send file
res.sendFile('/path/to/file.pdf');
// Download file
res.download('/path/to/file.pdf', 'document.pdf');
// Render view
res.render('index', { title: 'Home' });
// Redirect
res.redirect('/login');
res.redirect(301, 'https://example.com');
// End response
res.end();
});Setting Status and Headers:
app.get('/api/resource', (req, res) => {
// Set status code
res.status(201).json({ created: true });
// Set headers
res.set('Content-Type', 'application/json');
res.set({
'X-API-Version': '1.0',
'X-Rate-Limit': '100'
});
// Set cookie
res.cookie('name', 'value', {
maxAge: 900000,
httpOnly: true,
secure: true,
sameSite: 'strict'
});
// Clear cookie
res.clearCookie('name');
res.json({ success: true });
});Response Formats:
app.get('/api/users/:id', (req, res) => {
const user = { id: 1, name: 'John' };
res.format({
'text/plain': () => {
res.send(`${user.name}`);
},
'text/html': () => {
res.send(`<p>${user.name}</p>`);
},
'application/json': () => {
res.json(user);
},
default: () => {
res.status(406).send('Not Acceptable');
}
});
});Error Handling
Error-handling middleware functions have four arguments: (err, req, res, next).
Error-Handling Middleware:
// 404 handler
app.use((req, res, next) => {
res.status(404).json({ error: 'Not found' });
});
// Error handler (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
});Async Error Handling:
// Async wrapper utility
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Using async wrapper
app.get('/api/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
const error = new Error('User not found');
error.status = 404;
throw error;
}
res.json(user);
}));
// Custom error classes
class AppError extends Error {
constructor(message, status) {
super(message);
this.status = status;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(message = 'Resource not found') {
super(message, 404);
}
}
class ValidationError extends AppError {
constructor(message = 'Validation failed') {
super(message, 400);
}
}API Reference
Express Application Methods
app.use([path], middleware)
- Mounts middleware at the specified path
- If path is not specified, middleware is executed for every request
app.METHOD(path, [middleware...], handler)
- Routes HTTP requests (GET, POST, PUT, DELETE, etc.)
- Multiple middleware functions can be specified
app.route(path)
- Returns an instance of a single route for chaining HTTP verbs
app.listen(port, [hostname], [backlog], [callback])
- Binds and listens for connections on the specified host and port
app.param(name, callback)
- Adds callback triggers to route parameters
app.set(name, value)
- Assigns setting name to value
app.get(name)
- Returns the value of setting name
Router Methods
router.use([path], middleware)
- Mounts middleware for the router
router.METHOD(path, [middleware...], handler)
- Routes HTTP requests within the router
router.route(path)
- Returns a route instance for chaining
router.param(name, callback)
- Adds parameter callbacks
Request Properties
- req.body: Contains parsed request body (requires body-parser)
- req.params: Route parameters
- req.query: Parsed query string
- req.headers: Request headers
- req.cookies: Cookies (requires cookie-parser)
- req.method: HTTP method
- req.path: Request path
- req.url: Full URL
- req.ip: Remote IP address
- req.protocol: Request protocol (http or https)
Request Methods
- req.get(header): Returns header value
- req.is(type): Checks if content type matches
- req.accepts(types): Checks if types are acceptable
- req.range(size): Parses range header
Response Methods
- res.json(obj): Sends JSON response
- res.send(body): Sends response
- res.status(code): Sets status code
- res.sendStatus(code): Sets status and sends status message
- res.set(field, value): Sets response header
- res.cookie(name, value, options): Sets cookie
- res.clearCookie(name): Clears cookie
- res.redirect([status], path): Redirects to path
- res.render(view, locals): Renders view template
- res.sendFile(path): Sends file
- res.download(path, filename): Downloads file
Workflow Patterns
REST API Design
Complete REST API Example:
const express = require('express');
const router = express.Router();
// GET /api/users - List all users
router.get('/', asyncHandler(async (req, res) => {
const { page = 1, limit = 10, sort = 'createdAt' } = req.query;
const users = await User.find()
.sort(sort)
.limit(parseInt(limit))
.skip((parseInt(page) - 1) * parseInt(limit))
.select('-password');
const total = await User.countDocuments();
res.json({
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
}));
// GET /api/users/:id - Get single user
router.get('/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id).select('-password');
if (!user) {
throw new NotFoundError('User not found');
}
res.json({ data: user });
}));
// POST /api/users - Create user
router.post('/',
validateUser,
asyncHandler(async (req, res) => {
const { email, password, name } = req.body;
const existingUser = await User.findOne({ email });
if (existingUser) {
throw new ValidationError('Email already exists');
}
const user = await User.create({ email, password, name });
res.status(201).json({
data: user.toJSON(),
message: 'User created successfully'
});
})
);
// PUT /api/users/:id - Update user
router.put('/:id',
requireAuth,
validateUserUpdate,
asyncHandler(async (req, res) => {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
).select('-password');
if (!user) {
throw new NotFoundError('User not found');
}
res.json({
data: user,
message: 'User updated successfully'
});
})
);
// DELETE /api/users/:id - Delete user
router.delete('/:id',
requireAuth,
asyncHandler(async (req, res) => {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
throw new NotFoundError('User not found');
}
res.json({ message: 'User deleted successfully' });
})
);
module.exports = router;Authentication
JWT Authentication:
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
// Register
router.post('/register',
validateRegistration,
asyncHandler(async (req, res) => {
const { email, password, name } = req.body;
// Check if user exists
const existingUser = await User.findOne({ email });
if (existingUser) {
throw new ValidationError('Email already registered');
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const user = await User.create({
email,
password: hashedPassword,
name
});
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
data: {
user: user.toJSON(),
token
}
});
})
);
// Login
router.post('/login',
validateLogin,
asyncHandler(async (req, res) => {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
throw new ValidationError('Invalid credentials');
}
// Verify password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
throw new ValidationError('Invalid credentials');
}
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
data: {
user: user.toJSON(),
token
}
});
})
);
// Refresh token
router.post('/refresh',
asyncHandler(async (req, res) => {
const { refreshToken } = req.body;
if (!refreshToken) {
throw new ValidationError('Refresh token required');
}
const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
const token = jwt.sign(
{ userId: decoded.userId, email: decoded.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({ data: { token } });
})
);
// Auth middleware
function requireAuth(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new AuthenticationError('No token provided');
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
throw new AuthenticationError('Invalid token');
}
}
// Role-based authorization
function requireRole(...roles) {
return async (req, res, next) => {
const user = await User.findById(req.user.userId);
if (!user || !roles.includes(user.role)) {
throw new ForbiddenError('Insufficient permissions');
}
next();
};
}Validation
Input Validation with express-validator:
const { body, param, query, validationResult } = require('express-validator');
// Validation middleware
const validate = (validations) => {
return async (req, res, next) => {
await Promise.all(validations.map(validation => validation.run(req)));
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
error: 'Validation failed',
details: errors.array()
});
}
next();
};
};
// User validation rules
const userValidationRules = {
create: validate([
body('email')
.isEmail()
.normalizeEmail()
.withMessage('Invalid email address'),
body('password')
.isLength({ min: 8 })
.withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('Password must contain uppercase, lowercase, and number'),
body('name')
.trim()
.isLength({ min: 2, max: 50 })
.withMessage('Name must be between 2 and 50 characters')
]),
update: validate([
param('id')
.isMongoId()
.withMessage('Invalid user ID'),
body('email')
.optional()
.isEmail()
.normalizeEmail(),
body('name')
.optional()
.trim()
.isLength({ min: 2, max: 50 })
]),
list: validate([
query('page')
.optional()
.isInt({ min: 1 })
.toInt(),
query('limit')
.optional()
.isInt({ min: 1, max: 100 })
.toInt()
])
};
// Using validation
router.post('/users', userValidationRules.create, createUser);
router.put('/users/:id', userValidationRules.update, updateUser);
router.get('/users', userValidationRules.list, listUsers);Database Integration
MongoDB with Mongoose:
const mongoose = require('mongoose');
// Connect to database
async function connectDB() {
try {
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log('MongoDB connected');
} catch (error) {
console.error('MongoDB connection error:', error);
process.exit(1);
}
}
// User model
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
}
}, {
timestamps: true
});
userSchema.methods.toJSON = function() {
const user = this.toObject();
delete user.password;
return user;
};
const User = mongoose.model('User', userSchema);
// CRUD operations
router.get('/users', asyncHandler(async (req, res) => {
const users = await User.find().select('-password');
res.json({ data: users });
}));
router.post('/users', asyncHandler(async (req, res) => {
const user = await User.create(req.body);
res.status(201).json({ data: user });
}));
router.put('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
);
res.json({ data: user });
}));
router.delete('/users/:id', asyncHandler(async (req, res) => {
await User.findByIdAndDelete(req.params.id);
res.json({ message: 'User deleted' });
}));Testing
API Testing with Jest and Supertest:
const request = require('supertest');
const app = require('../app');
const User = require('../models/User');
describe('User API', () => {
beforeEach(async () => {
await User.deleteMany({});
});
describe('POST /api/users', () => {
it('should create a new user', async () => {
const userData = {
email: 'test@example.com',
password: 'Password123',
name: 'Test User'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body.data).toHaveProperty('email', userData.email);
expect(response.body.data).not.toHaveProperty('password');
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: 'invalid-email',
password: 'Password123',
name: 'Test'
})
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
describe('GET /api/users/:id', () => {
it('should return user by id', async () => {
const user = await User.create({
email: 'test@example.com',
password: 'hashed',
name: 'Test User'
});
const response = await request(app)
.get(`/api/users/${user._id}`)
.expect(200);
expect(response.body.data).toHaveProperty('email', user.email);
});
it('should return 404 for non-existent user', async () => {
const response = await request(app)
.get('/api/users/507f1f77bcf86cd799439011')
.expect(404);
expect(response.body).toHaveProperty('error');
});
});
describe('Authentication', () => {
it('should require authentication for protected routes', async () => {
await request(app)
.get('/api/protected')
.expect(401);
});
it('should allow access with valid token', async () => {
const token = jwt.sign({ userId: '123' }, process.env.JWT_SECRET);
await request(app)
.get('/api/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
});
});
});Best Practices
Security
Security Headers with Helmet:
const helmet = require('helmet');
// Use helmet for security headers
app.use(helmet());
// Custom configuration
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:']
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));CORS Configuration:
const cors = require('cors');
// Allow all origins (development only)
app.use(cors());
// Production configuration
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
maxAge: 86400 // 24 hours
}));
// Dynamic origin validation
app.use(cors({
origin: (origin, callback) => {
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));Rate Limiting:
const rateLimit = require('express-rate-limit');
// General API rate limiter
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP',
standardHeaders: true,
legacyHeaders: false
});
app.use('/api/', apiLimiter);
// Strict rate limiter for authentication
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true
});
app.use('/api/login', authLimiter);
app.use('/api/register', authLimiter);
// Custom key generator
const customLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 100,
keyGenerator: (req) => {
return req.user?.id || req.ip;
}
});Input Sanitization:
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
// Prevent NoSQL injection
app.use(mongoSanitize());
// Prevent XSS attacks
app.use(xss());
// Custom sanitization middleware
function sanitizeInput(req, res, next) {
if (req.body) {
Object.keys(req.body).forEach(key => {
if (typeof req.body[key] === 'string') {
req.body[key] = req.body[key].trim();
}
});
}
next();
}
app.use(sanitizeInput);Performance
Response Compression:
const compression = require('compression');
// Enable compression
app.use(compression({
level: 6,
threshold: 1024,
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
}
}));Caching:
// Simple in-memory cache
const cache = new Map();
function cacheMiddleware(duration) {
return (req, res, next) => {
const key = req.originalUrl;
const cached = cache.get(key);
if (cached && Date.now() < cached.expiry) {
return res.json(cached.data);
}
res.originalJson = res.json;
res.json = (data) => {
cache.set(key, {
data,
expiry: Date.now() + duration * 1000
});
res.originalJson(data);
};
next();
};
}
// Use cache
app.get('/api/users', cacheMiddleware(60), getUsers);
// Redis cache
const redis = require('redis');
const client = redis.createClient();
async function redisCache(duration) {
return async (req, res, next) => {
const key = `cache:${req.originalUrl}`;
const cached = await client.get(key);
if (cached) {
return res.json(JSON.parse(cached));
}
res.originalJson = res.json;
res.json = async (data) => {
await client.setEx(key, duration, JSON.stringify(data));
res.originalJson(data);
};
next();
};
}Request Timeout:
function timeout(ms) {
return (req, res, next) => {
req.setTimeout(ms, () => {
res.status(408).json({ error: 'Request timeout' });
});
next();
};
}
app.use(timeout(30000)); // 30 secondsError Handling
Centralized Error Handling:
// 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) {
super(message, 400);
}
}
class AuthenticationError extends AppError {
constructor(message) {
super(message, 401);
}
}
class NotFoundError extends AppError {
constructor(message) {
super(message, 404);
}
}
// Error handler
function errorHandler(err, req, res, next) {
let error = { ...err };
error.message = err.message;
// Log error
console.error(err);
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(e => e.message).join(', ');
error = new ValidationError(message);
}
// Mongoose duplicate key
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
error = new ValidationError(`${field} already exists`);
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
error = new AuthenticationError('Invalid token');
}
if (err.name === 'TokenExpiredError') {
error = new AuthenticationError('Token expired');
}
res.status(error.statusCode || 500).json({
error: {
message: error.message || 'Server error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
}
app.use(errorHandler);Logging
Morgan and Winston:
const morgan = require('morgan');
const winston = require('winston');
// Winston logger
const logger = winston.createLogger({
level: process.env.LOG_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' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
// Morgan HTTP logging
app.use(morgan('combined', {
stream: {
write: (message) => logger.info(message.trim())
}
}));
// Custom logging middleware
app.use((req, res, next) => {
logger.info({
method: req.method,
url: req.url,
ip: req.ip,
userAgent: req.get('user-agent')
});
next();
});API Versioning
URL Versioning:
// Version 1 routes
const v1Router = express.Router();
v1Router.get('/users', getUsersV1);
app.use('/api/v1', v1Router);
// Version 2 routes
const v2Router = express.Router();
v2Router.get('/users', getUsersV2);
app.use('/api/v2', v2Router);Header Versioning:
function apiVersion(version) {
return (req, res, next) => {
const requestedVersion = req.get('API-Version') || '1.0';
if (requestedVersion === version) {
next();
} else {
next('route');
}
};
}
app.get('/api/users', apiVersion('1.0'), getUsersV1);
app.get('/api/users', apiVersion('2.0'), getUsersV2);Examples
1. Basic Express Server
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.json({ message: 'Hello Express!' });
});
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString()
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});2. Complete REST API
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Models
const Product = mongoose.model('Product', {
name: { type: String, required: true },
price: { type: Number, required: true },
description: String,
inStock: { type: Boolean, default: true }
});
// Routes
app.get('/api/products', async (req, res, next) => {
try {
const products = await Product.find();
res.json({ data: products });
} catch (error) {
next(error);
}
});
app.get('/api/products/:id', async (req, res, next) => {
try {
const product = await Product.findById(req.params.id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json({ data: product });
} catch (error) {
next(error);
}
});
app.post('/api/products', async (req, res, next) => {
try {
const product = await Product.create(req.body);
res.status(201).json({ data: product });
} catch (error) {
next(error);
}
});
app.put('/api/products/:id', async (req, res, next) => {
try {
const product = await Product.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json({ data: product });
} catch (error) {
next(error);
}
});
app.delete('/api/products/:id', async (req, res, next) => {
try {
const product = await Product.findByIdAndDelete(req.params.id);
if (!product) {
return res.status(404).json({ error: 'Product not found' });
}
res.json({ message: 'Product deleted' });
} catch (error) {
next(error);
}
});
// Error handler
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: err.message });
});
// Start server
mongoose.connect('mongodb://localhost/shop')
.then(() => {
app.listen(3000, () => console.log('Server running'));
});3. Authentication System
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
const users = new Map(); // In-memory storage
// Register
app.post('/api/register', async (req, res) => {
const { email, password, name } = req.body;
if (users.has(email)) {
return res.status(400).json({ error: 'Email already exists' });
}
const hashedPassword = await bcrypt.hash(password, 10);
users.set(email, {
email,
password: hashedPassword,
name,
id: Date.now().toString()
});
res.status(201).json({ message: 'User created' });
});
// Login
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = users.get(email);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign(
{ userId: user.id, email: user.email },
'secret-key',
{ expiresIn: '24h' }
);
res.json({ token });
});
// Protected route
app.get('/api/profile', (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
try {
const decoded = jwt.verify(token, 'secret-key');
const user = Array.from(users.values()).find(u => u.id === decoded.userId);
res.json({
email: user.email,
name: user.name
});
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});
app.listen(3000);See EXAMPLES.md for 15+ additional examples covering file uploads, CORS, rate limiting, WebSockets, testing, deployment, and more.
Summary
This Express.js development skill covers:
1. Core Concepts: Application setup, routing, middleware, request/response handling, error handling 2. API Reference: Complete reference for Express methods and properties 3. Workflow Patterns: REST API design, authentication, validation, database integration, testing 4. Best Practices: Security (helmet, CORS, rate limiting), performance (compression, caching), error handling, logging, API versioning 5. Real-world Examples: Complete implementations for common use cases
The patterns and examples are based on Express.js best practices (Trust Score: 9) and represent modern Node.js backend development standards.
Express.js Examples
Comprehensive code examples demonstrating real-world Express.js patterns and use cases.
Table of Contents
1. Basic Server Setup 2. Routing Patterns 3. Middleware Examples 4. Authentication & Authorization 5. Input Validation 6. Database Integration 7. File Uploads 8. Cookies & Sessions 9. CORS Configuration 10. Rate Limiting 11. API Versioning 12. Error Handling 13. Logging 14. Testing 15. Security Best Practices 16. WebSocket Integration 17. Email Service 18. Pagination 19. Search & Filtering 20. Deployment
---
1. Basic Server Setup
Simple Express Server
// server.js
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Basic route
app.get('/', (req, res) => {
res.json({
message: 'Welcome to Express API',
version: '1.0.0'
});
});
// Health check
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
uptime: process.uptime(),
timestamp: new Date().toISOString()
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({
error: 'Route not found'
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});Modular Server Setup
// src/app.js
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const app = express();
// Security middleware
app.use(helmet());
// CORS
app.use(cors());
// Logging
app.use(morgan('combined'));
// Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/users', require('./routes/users'));
app.use('/api/posts', require('./routes/posts'));
// Error handling
app.use(require('./middleware/errorHandler'));
module.exports = app;
// server.js
const app = require('./src/app');
const config = require('./src/config');
app.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});---
2. Routing Patterns
Basic Router Module
// routes/users.js
const express = require('express');
const router = express.Router();
// GET all users
router.get('/', (req, res) => {
res.json({ users: [] });
});
// GET user by ID
router.get('/:id', (req, res) => {
const { id } = req.params;
res.json({ userId: id });
});
// POST create user
router.post('/', (req, res) => {
const { name, email } = req.body;
res.status(201).json({ name, email });
});
// PUT update user
router.put('/:id', (req, res) => {
const { id } = req.params;
const updates = req.body;
res.json({ id, ...updates });
});
// DELETE user
router.delete('/:id', (req, res) => {
const { id } = req.params;
res.json({ message: `User ${id} deleted` });
});
module.exports = router;Nested Routes
// routes/posts.js
const express = require('express');
const router = express.Router();
// Comments router
const commentsRouter = express.Router({ mergeParams: true });
// POST comment on post
commentsRouter.post('/', (req, res) => {
const { postId } = req.params;
const { text } = req.body;
res.status(201).json({ postId, text });
});
// GET comments for post
commentsRouter.get('/', (req, res) => {
const { postId } = req.params;
res.json({ postId, comments: [] });
});
// Mount comments router
router.use('/:postId/comments', commentsRouter);
// Post routes
router.get('/', (req, res) => {
res.json({ posts: [] });
});
router.get('/:postId', (req, res) => {
const { postId } = req.params;
res.json({ postId });
});
module.exports = router;Route Parameter Validation
// routes/users.js
const express = require('express');
const router = express.Router();
// Parameter validation middleware
router.param('userId', (req, res, next, id) => {
// Validate ID format
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({
error: 'Invalid user ID format'
});
}
// Attach validated ID
req.userId = id;
next();
});
// Use validated parameter
router.get('/:userId', async (req, res) => {
const user = await User.findById(req.userId);
res.json({ user });
});
module.exports = router;Route Chaining
const router = express.Router();
router.route('/users/:id')
.get((req, res) => {
res.json({ message: 'Get user' });
})
.put((req, res) => {
res.json({ message: 'Update user' });
})
.delete((req, res) => {
res.json({ message: 'Delete user' });
});
module.exports = router;---
3. Middleware Examples
Request Logger
// middleware/logger.js
function requestLogger(req, res, next) {
const start = Date.now();
// Log when response finishes
res.on('finish', () => {
const duration = Date.now() - start;
console.log(
`${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`
);
});
next();
}
module.exports = requestLogger;
// Usage
app.use(requestLogger);Request ID Middleware
// middleware/requestId.js
const { v4: uuidv4 } = require('uuid');
function requestId(req, res, next) {
req.id = req.get('X-Request-ID') || uuidv4();
res.set('X-Request-ID', req.id);
next();
}
module.exports = requestId;Timing Middleware
// middleware/timing.js
function timing(req, res, next) {
const start = process.hrtime.bigint();
res.on('finish', () => {
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000; // Convert to ms
res.set('X-Response-Time', `${duration.toFixed(2)}ms`);
});
next();
}
module.exports = timing;Conditional Middleware
// middleware/conditional.js
function conditionalMiddleware(condition, middleware) {
return (req, res, next) => {
if (condition(req)) {
return middleware(req, res, next);
}
next();
};
}
// Usage
app.use(conditionalMiddleware(
req => req.path.startsWith('/api'),
requireAuth
));Error Async Wrapper
// utils/asyncHandler.js
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
module.exports = asyncHandler;
// Usage
router.get('/users', asyncHandler(async (req, res) => {
const users = await User.find();
res.json({ data: users });
}));---
4. Authentication & Authorization
JWT Authentication
// middleware/auth.js
const jwt = require('jsonwebtoken');
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'No token provided'
});
}
const token = authHeader.substring(7);
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({
error: 'Invalid or expired token'
});
}
}
module.exports = { authenticate };Complete Auth System
// routes/auth.js
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const { body, validationResult } = require('express-validator');
const User = require('../models/User');
const asyncHandler = require('../utils/asyncHandler');
const router = express.Router();
// Register
router.post('/register',
[
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
body('name').trim().notEmpty()
],
asyncHandler(async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password, name } = req.body;
// Check existing user
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).json({
error: 'Email already registered'
});
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const user = await User.create({
email,
password: hashedPassword,
name
});
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.status(201).json({
data: {
user: {
id: user._id,
email: user.email,
name: user.name
},
token
}
});
})
);
// Login
router.post('/login',
[
body('email').isEmail(),
body('password').notEmpty()
],
asyncHandler(async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({
error: 'Invalid credentials'
});
}
// Verify password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({
error: 'Invalid credentials'
});
}
// Generate token
const token = jwt.sign(
{ userId: user._id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
res.json({
data: {
user: {
id: user._id,
email: user.email,
name: user.name
},
token
}
});
})
);
// Get current user
router.get('/me',
authenticate,
asyncHandler(async (req, res) => {
const user = await User.findById(req.user.userId).select('-password');
res.json({ data: user });
})
);
module.exports = router;Role-Based Authorization
// middleware/authorize.js
function authorize(...roles) {
return async (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: 'Authentication required'
});
}
const user = await User.findById(req.user.userId);
if (!user || !roles.includes(user.role)) {
return res.status(403).json({
error: 'Insufficient permissions'
});
}
next();
};
}
module.exports = authorize;
// Usage
router.delete('/users/:id',
authenticate,
authorize('admin', 'moderator'),
deleteUser
);API Key Authentication
// middleware/apiKey.js
function validateApiKey(req, res, next) {
const apiKey = req.get('X-API-Key');
if (!apiKey) {
return res.status(401).json({
error: 'API key required'
});
}
if (!isValidApiKey(apiKey)) {
return res.status(401).json({
error: 'Invalid API key'
});
}
next();
}
async function isValidApiKey(key) {
const apiKey = await ApiKey.findOne({ key, active: true });
return !!apiKey;
}
module.exports = validateApiKey;---
5. Input Validation
Express Validator
// validators/userValidator.js
const { body, param, query } = require('express-validator');
const userValidator = {
create: [
body('email')
.isEmail()
.normalizeEmail()
.withMessage('Invalid email address'),
body('password')
.isLength({ min: 8 })
.withMessage('Password must be at least 8 characters')
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
.withMessage('Password must contain uppercase, lowercase, and number'),
body('name')
.trim()
.isLength({ min: 2, max: 50 })
.withMessage('Name must be between 2 and 50 characters'),
body('age')
.optional()
.isInt({ min: 18, max: 120 })
.withMessage('Age must be between 18 and 120')
],
update: [
param('id')
.isMongoId()
.withMessage('Invalid user ID'),
body('email')
.optional()
.isEmail()
.normalizeEmail(),
body('name')
.optional()
.trim()
.isLength({ min: 2, max: 50 })
],
list: [
query('page')
.optional()
.isInt({ min: 1 })
.toInt()
.withMessage('Page must be a positive integer'),
query('limit')
.optional()
.isInt({ min: 1, max: 100 })
.toInt()
.withMessage('Limit must be between 1 and 100'),
query('sort')
.optional()
.isIn(['name', 'email', 'createdAt'])
.withMessage('Invalid sort field')
]
};
module.exports = userValidator;Validation Middleware
// middleware/validate.js
const { validationResult } = require('express-validator');
function validate(req, res, next) {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
error: 'Validation failed',
details: errors.array().map(err => ({
field: err.param,
message: err.msg,
value: err.value
}))
});
}
next();
}
module.exports = validate;
// Usage
router.post('/users',
userValidator.create,
validate,
createUser
);Custom Validators
// validators/custom.js
const { body } = require('express-validator');
const User = require('../models/User');
const customValidators = {
uniqueEmail: body('email').custom(async (email) => {
const user = await User.findOne({ email });
if (user) {
throw new Error('Email already exists');
}
return true;
}),
strongPassword: body('password').custom((password) => {
if (!/[A-Z]/.test(password)) {
throw new Error('Password must contain uppercase letter');
}
if (!/[a-z]/.test(password)) {
throw new Error('Password must contain lowercase letter');
}
if (!/[0-9]/.test(password)) {
throw new Error('Password must contain number');
}
if (!/[!@#$%^&*]/.test(password)) {
throw new Error('Password must contain special character');
}
return true;
}),
matchingPasswords: body('confirmPassword').custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error('Passwords do not match');
}
return true;
})
};
module.exports = customValidators;---
6. Database Integration
MongoDB with Mongoose
// config/database.js
const mongoose = require('mongoose');
async function connectDB() {
try {
await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log('MongoDB connected successfully');
} catch (error) {
console.error('MongoDB connection error:', error);
process.exit(1);
}
}
// Handle connection events
mongoose.connection.on('disconnected', () => {
console.log('MongoDB disconnected');
});
mongoose.connection.on('error', (err) => {
console.error('MongoDB error:', err);
});
module.exports = connectDB;
// models/User.js
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
lowercase: true,
trim: true
},
password: {
type: String,
required: true
},
name: {
type: String,
required: true,
trim: true
},
role: {
type: String,
enum: ['user', 'admin', 'moderator'],
default: 'user'
},
active: {
type: Boolean,
default: true
}
}, {
timestamps: true
});
// Virtual field
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// Instance method
userSchema.methods.toJSON = function() {
const user = this.toObject();
delete user.password;
return user;
};
// Static method
userSchema.statics.findByEmail = function(email) {
return this.findOne({ email });
};
// Index
userSchema.index({ email: 1 });
module.exports = mongoose.model('User', userSchema);
// controllers/userController.js
const User = require('../models/User');
const asyncHandler = require('../utils/asyncHandler');
exports.getUsers = asyncHandler(async (req, res) => {
const { page = 1, limit = 10, sort = 'createdAt' } = req.query;
const users = await User.find({ active: true })
.select('-password')
.sort(sort)
.limit(parseInt(limit))
.skip((parseInt(page) - 1) * parseInt(limit));
const total = await User.countDocuments({ active: true });
res.json({
data: users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
});
exports.createUser = asyncHandler(async (req, res) => {
const user = await User.create(req.body);
res.status(201).json({ data: user });
});
exports.updateUser = asyncHandler(async (req, res) => {
const user = await User.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true, runValidators: true }
).select('-password');
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ data: user });
});
exports.deleteUser = asyncHandler(async (req, res) => {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ message: 'User deleted successfully' });
});PostgreSQL with Sequelize
// config/database.js
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASSWORD,
{
host: process.env.DB_HOST,
dialect: 'postgres',
logging: process.env.NODE_ENV === 'development' ? console.log : false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
}
);
async function connectDB() {
try {
await sequelize.authenticate();
console.log('PostgreSQL connected successfully');
await sequelize.sync({ alter: process.env.NODE_ENV === 'development' });
} catch (error) {
console.error('Database connection error:', error);
process.exit(1);
}
}
module.exports = { sequelize, connectDB };
// models/User.js
const { DataTypes } = require('sequelize');
const { sequelize } = require('../config/database');
const User = sequelize.define('User', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.ENUM('user', 'admin', 'moderator'),
defaultValue: 'user'
},
active: {
type: DataTypes.BOOLEAN,
defaultValue: true
}
}, {
timestamps: true,
tableName: 'users'
});
// Instance method
User.prototype.toJSON = function() {
const values = { ...this.get() };
delete values.password;
return values;
};
module.exports = User;---
7. File Uploads
Multer Configuration
// middleware/upload.js
const multer = require('multer');
const path = require('path');
// Storage configuration
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
}
});
// File filter
const fileFilter = (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png|gif|pdf/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (extname && mimetype) {
cb(null, true);
} else {
cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and PDF allowed.'));
}
};
const upload = multer({
storage,
fileFilter,
limits: {
fileSize: 5 * 1024 * 1024 // 5MB
}
});
module.exports = upload;
// routes/upload.js
const express = require('express');
const upload = require('../middleware/upload');
const router = express.Router();
// Single file upload
router.post('/single', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
res.json({
message: 'File uploaded successfully',
file: {
filename: req.file.filename,
originalname: req.file.originalname,
size: req.file.size,
path: req.file.path
}
});
});
// Multiple files upload
router.post('/multiple', upload.array('files', 5), (req, res) => {
if (!req.files || req.files.length === 0) {
return res.status(400).json({ error: 'No files uploaded' });
}
const files = req.files.map(file => ({
filename: file.filename,
originalname: file.originalname,
size: file.size,
path: file.path
}));
res.json({
message: 'Files uploaded successfully',
files
});
});
// Multiple fields
router.post('/fields',
upload.fields([
{ name: 'avatar', maxCount: 1 },
{ name: 'gallery', maxCount: 5 }
]),
(req, res) => {
res.json({
message: 'Files uploaded successfully',
avatar: req.files.avatar,
gallery: req.files.gallery
});
}
);
module.exports = router;Image Upload with Sharp
// middleware/imageUpload.js
const multer = require('multer');
const sharp = require('sharp');
const path = require('path');
const fs = require('fs').promises;
const upload = multer({
storage: multer.memoryStorage(),
fileFilter: (req, file, cb) => {
const allowedTypes = /jpeg|jpg|png/;
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = allowedTypes.test(file.mimetype);
if (extname && mimetype) {
cb(null, true);
} else {
cb(new Error('Only images allowed'));
}
},
limits: { fileSize: 5 * 1024 * 1024 }
});
async function processImage(req, res, next) {
if (!req.file) return next();
const filename = `${Date.now()}-${req.file.originalname}`;
try {
await sharp(req.file.buffer)
.resize(800, 600, { fit: 'inside', withoutEnlargement: true })
.jpeg({ quality: 90 })
.toFile(`uploads/${filename}`);
// Create thumbnail
await sharp(req.file.buffer)
.resize(200, 200, { fit: 'cover' })
.jpeg({ quality: 80 })
.toFile(`uploads/thumbnails/${filename}`);
req.processedImage = {
filename,
path: `uploads/${filename}`,
thumbnail: `uploads/thumbnails/${filename}`
};
next();
} catch (error) {
next(error);
}
}
module.exports = { upload, processImage };---
8. Cookies & Sessions
Cookie Management
// app.js
const cookieParser = require('cookie-parser');
app.use(cookieParser(process.env.COOKIE_SECRET));
// routes/cookies.js
const express = require('express');
const router = express.Router();
// Set cookie
router.get('/set', (req, res) => {
res.cookie('user', 'john', {
maxAge: 900000, // 15 minutes
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
});
res.json({ message: 'Cookie set' });
});
// Read cookie
router.get('/read', (req, res) => {
const { user } = req.cookies;
res.json({ user });
});
// Clear cookie
router.get('/clear', (req, res) => {
res.clearCookie('user');
res.json({ message: 'Cookie cleared' });
});
module.exports = router;Session Management
// app.js
const session = require('express-session');
const MongoStore = require('connect-mongo');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URI,
ttl: 24 * 60 * 60 // 1 day
}),
cookie: {
maxAge: 24 * 60 * 60 * 1000, // 1 day
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict'
}
}));
// routes/session.js
router.post('/login', (req, res) => {
const { username, password } = req.body;
// Validate credentials
if (isValidCredentials(username, password)) {
req.session.user = {
username,
loggedInAt: new Date()
};
res.json({ message: 'Logged in successfully' });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
router.get('/profile', (req, res) => {
if (!req.session.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
res.json({ user: req.session.user });
});
router.post('/logout', (req, res) => {
req.session.destroy((err) => {
if (err) {
return res.status(500).json({ error: 'Logout failed' });
}
res.clearCookie('connect.sid');
res.json({ message: 'Logged out successfully' });
});
});---
9. CORS Configuration
// config/cors.js
const cors = require('cors');
// Basic CORS
const basicCors = cors();
// Custom CORS
const customCors = cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count'],
credentials: true,
maxAge: 86400 // 24 hours
});
// Dynamic CORS
const dynamicCors = cors({
origin: (origin, callback) => {
const allowedOrigins = [
'http://localhost:3000',
'https://example.com',
'https://app.example.com'
];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true
});
// Conditional CORS
function conditionalCors(req, res, next) {
if (req.path.startsWith('/api/public')) {
return cors()(req, res, next);
}
next();
}
module.exports = { basicCors, customCors, dynamicCors, conditionalCors };---
10. Rate Limiting
// middleware/rateLimiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const redis = require('redis');
// Basic rate limiter
const basicLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
message: 'Too many requests from this IP',
standardHeaders: true,
legacyHeaders: false
});
// Strict rate limiter for auth
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
skipSuccessfulRequests: true,
message: 'Too many login attempts'
});
// Redis-based rate limiter
const redisClient = redis.createClient();
const redisLimiter = rateLimit({
store: new RedisStore({
client: redisClient,
prefix: 'rl:'
}),
windowMs: 60 * 60 * 1000, // 1 hour
max: 1000
});
// Custom key generator (per user)
const userLimiter = rateLimit({
windowMs: 60 * 60 * 1000,
max: 100,
keyGenerator: (req) => {
return req.user?.id || req.ip;
},
handler: (req, res) => {
res.status(429).json({
error: 'Rate limit exceeded',
retryAfter: req.rateLimit.resetTime
});
}
});
// Slow down instead of blocking
const slowDown = require('express-slow-down');
const speedLimiter = slowDown({
windowMs: 15 * 60 * 1000,
delayAfter: 50,
delayMs: 500
});
module.exports = {
basicLimiter,
authLimiter,
redisLimiter,
userLimiter,
speedLimiter
};
// Usage
app.use('/api/', basicLimiter);
app.use('/api/login', authLimiter);
app.use('/api/register', authLimiter);---
11. API Versioning
URL Versioning
// app.js
const v1Routes = require('./routes/v1');
const v2Routes = require('./routes/v2');
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
// routes/v1/index.js
const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/posts', require('./posts'));
module.exports = router;
// routes/v2/index.js
const express = require('express');
const router = express.Router();
router.use('/users', require('./users'));
router.use('/posts', require('./posts'));
module.exports = router;Header Versioning
// middleware/apiVersion.js
function apiVersion(version) {
return (req, res, next) => {
const requestedVersion = req.get('API-Version') || '1.0';
if (requestedVersion === version) {
next();
} else {
next('route');
}
};
}
module.exports = apiVersion;
// Usage
app.get('/api/users', apiVersion('1.0'), getUsersV1);
app.get('/api/users', apiVersion('2.0'), getUsersV2);
app.get('/api/users', (req, res) => {
res.status(400).json({ error: 'Unsupported API version' });
});Accept Header Versioning
// middleware/acceptVersion.js
function acceptVersion(version) {
return (req, res, next) => {
const accept = req.get('Accept');
if (accept && accept.includes(`application/vnd.api.v${version}+json`)) {
next();
} else {
next('route');
}
};
}
// Usage
app.get('/api/users',
acceptVersion('1'),
getUsersV1
);
app.get('/api/users',
acceptVersion('2'),
getUsersV2
);---
12. Error Handling
Custom Error Classes
// utils/errors.js
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 = 'Validation failed') {
super(message, 400);
}
}
class AuthenticationError extends AppError {
constructor(message = 'Authentication required') {
super(message, 401);
}
}
class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(message, 403);
}
}
class NotFoundError extends AppError {
constructor(message = 'Resource not found') {
super(message, 404);
}
}
class ConflictError extends AppError {
constructor(message = 'Resource conflict') {
super(message, 409);
}
}
module.exports = {
AppError,
ValidationError,
AuthenticationError,
ForbiddenError,
NotFoundError,
ConflictError
};Error Handler Middleware
// middleware/errorHandler.js
const { AppError } = require('../utils/errors');
function errorHandler(err, req, res, next) {
let error = { ...err };
error.message = err.message;
// Log error
console.error(err);
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map(e => e.message).join(', ');
error = new AppError(message, 400);
}
// Mongoose duplicate key
if (err.code === 11000) {
const field = Object.keys(err.keyValue)[0];
error = new AppError(`${field} already exists`, 409);
}
// Mongoose cast error
if (err.name === 'CastError') {
error = new AppError('Invalid ID format', 400);
}
// JWT errors
if (err.name === 'JsonWebTokenError') {
error = new AppError('Invalid token', 401);
}
if (err.name === 'TokenExpiredError') {
error = new AppError('Token expired', 401);
}
// Multer errors
if (err.name === 'MulterError') {
if (err.code === 'LIMIT_FILE_SIZE') {
error = new AppError('File too large', 400);
}
}
res.status(error.statusCode || 500).json({
error: {
message: error.message || 'Server error',
...(process.env.NODE_ENV === 'development' && {
stack: err.stack,
details: err
})
}
});
}
// 404 handler
function notFoundHandler(req, res, next) {
res.status(404).json({
error: {
message: 'Route not found',
path: req.originalUrl
}
});
}
module.exports = { errorHandler, notFoundHandler };---
13. Logging
Winston Logger
// config/logger.js
const winston = require('winston');
const path = require('path');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: { service: 'api' },
transports: [
new winston.transports.File({
filename: path.join('logs', 'error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5
}),
new winston.transports.File({
filename: path.join('logs', 'combined.log'),
maxsize: 5242880,
maxFiles: 5
})
]
});
// Console logging in development
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}));
}
module.exports = logger;
// middleware/requestLogger.js
const logger = require('../config/logger');
function requestLogger(req, res, next) {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
logger.info({
method: req.method,
url: req.originalUrl,
status: res.statusCode,
duration: `${duration}ms`,
ip: req.ip,
userAgent: req.get('user-agent')
});
});
next();
}
module.exports = requestLogger;Morgan HTTP Logger
// config/morgan.js
const morgan = require('morgan');
const logger = require('./logger');
// Custom token
morgan.token('id', (req) => req.id);
// Custom format
const format = ':id :method :url :status :response-time ms - :res[content-length]';
// Stream to Winston
const stream = {
write: (message) => logger.http(message.trim())
};
const morganMiddleware = morgan(format, { stream });
module.exports = morganMiddleware;---
14. Testing
Jest Configuration
// jest.config.js
module.exports = {
testEnvironment: 'node',
coveragePathIgnorePatterns: ['/node_modules/'],
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js',
'!src/**/*.spec.js'
],
setupFilesAfterEnv: ['./tests/setup.js']
};
// tests/setup.js
const mongoose = require('mongoose');
beforeAll(async () => {
await mongoose.connect(process.env.TEST_DATABASE_URL);
});
afterAll(async () => {
await mongoose.connection.close();
});
afterEach(async () => {
const collections = mongoose.connection.collections;
for (const key in collections) {
await collections[key].deleteMany();
}
});API Tests
// tests/auth.test.js
const request = require('supertest');
const app = require('../src/app');
const User = require('../src/models/User');
describe('Authentication', () => {
describe('POST /api/auth/register', () => {
it('should register a new user', async () => {
const userData = {
email: 'test@example.com',
password: 'Password123!',
name: 'Test User'
};
const response = await request(app)
.post('/api/auth/register')
.send(userData)
.expect(201);
expect(response.body.data).toHaveProperty('token');
expect(response.body.data.user).toHaveProperty('email', userData.email);
expect(response.body.data.user).not.toHaveProperty('password');
const user = await User.findOne({ email: userData.email });
expect(user).toBeTruthy();
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'invalid-email',
password: 'Password123!',
name: 'Test'
})
.expect(400);
expect(response.body).toHaveProperty('errors');
});
it('should return 400 for duplicate email', async () => {
await User.create({
email: 'existing@example.com',
password: 'hashed',
name: 'Existing'
});
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'existing@example.com',
password: 'Password123!',
name: 'New User'
})
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
describe('POST /api/auth/login', () => {
beforeEach(async () => {
await request(app)
.post('/api/auth/register')
.send({
email: 'test@example.com',
password: 'Password123!',
name: 'Test User'
});
});
it('should login with valid credentials', async () => {
const response = await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'Password123!'
})
.expect(200);
expect(response.body.data).toHaveProperty('token');
expect(response.body.data.user).toHaveProperty('email', 'test@example.com');
});
it('should return 401 for invalid credentials', async () => {
await request(app)
.post('/api/auth/login')
.send({
email: 'test@example.com',
password: 'WrongPassword'
})
.expect(401);
});
});
describe('GET /api/auth/me', () => {
let token;
beforeEach(async () => {
const response = await request(app)
.post('/api/auth/register')
.send({
email: 'test@example.com',
password: 'Password123!',
name: 'Test User'
});
token = response.body.data.token;
});
it('should return current user with valid token', async () => {
const response = await request(app)
.get('/api/auth/me')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.body.data).toHaveProperty('email', 'test@example.com');
});
it('should return 401 without token', async () => {
await request(app)
.get('/api/auth/me')
.expect(401);
});
});
});Unit Tests
// tests/unit/validators.test.js
const { validateEmail, validatePassword } = require('../../src/utils/validators');
describe('Validators', () => {
describe('validateEmail', () => {
it('should validate correct email', () => {
expect(validateEmail('test@example.com')).toBe(true);
});
it('should reject invalid email', () => {
expect(validateEmail('invalid-email')).toBe(false);
expect(validateEmail('test@')).toBe(false);
expect(validateEmail('@example.com')).toBe(false);
});
});
describe('validatePassword', () => {
it('should validate strong password', () => {
expect(validatePassword('Password123!')).toBe(true);
});
it('should reject weak password', () => {
expect(validatePassword('short')).toBe(false);
expect(validatePassword('nouppercase123!')).toBe(false);
expect(validatePassword('NOLOWERCASE123!')).toBe(false);
expect(validatePassword('NoNumbers!')).toBe(false);
});
});
});---
15. Security Best Practices
// app.js - Security Setup
const express = require('express');
const helmet = require('helmet');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
const hpp = require('hpp');
const cors = require('cors');
const rateLimit = require('express-rate-limit');
const app = express();
// Security headers
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:']
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// CORS
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true
}));
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
});
app.use('/api', limiter);
// Body parsing with size limits
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: true, limit: '10kb' }));
// Data sanitization against NoSQL injection
app.use(mongoSanitize());
// Data sanitization against XSS
app.use(xss());
// Prevent HTTP parameter pollution
app.use(hpp({
whitelist: ['sort', 'filter']
}));
// Disable X-Powered-By header
app.disable('x-powered-by');
module.exports = app;---
16. WebSocket Integration
// server.js
const express = require('express');
const http = require('http');
const socketIo = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
cors: {
origin: process.env.CLIENT_URL,
methods: ['GET', 'POST']
}
});
// Socket.io middleware
io.use((socket, next) => {
const token = socket.handshake.auth.token;
if (isValidToken(token)) {
socket.userId = getUserIdFromToken(token);
next();
} else {
next(new Error('Authentication error'));
}
});
// Socket.io events
io.on('connection', (socket) => {
console.log('User connected:', socket.userId);
socket.on('join-room', (roomId) => {
socket.join(roomId);
io.to(roomId).emit('user-joined', socket.userId);
});
socket.on('send-message', (data) => {
io.to(data.roomId).emit('new-message', {
userId: socket.userId,
message: data.message,
timestamp: new Date()
});
});
socket.on('disconnect', () => {
console.log('User disconnected:', socket.userId);
});
});
server.listen(3000);---
17. Email Service
// services/emailService.js
const nodemailer = require('nodemailer');
class EmailService {
constructor() {
this.transporter = nodemailer.createTransporter({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
}
async sendEmail({ to, subject, html, text }) {
const mailOptions = {
from: process.env.EMAIL_FROM,
to,
subject,
html,
text
};
return await this.transporter.sendMail(mailOptions);
}
async sendWelcomeEmail(user) {
return await this.sendEmail({
to: user.email,
subject: 'Welcome!',
html: `<h1>Welcome ${user.name}!</h1>`,
text: `Welcome ${user.name}!`
});
}
async sendPasswordReset(user, token) {
const resetUrl = `${process.env.CLIENT_URL}/reset-password/${token}`;
return await this.sendEmail({
to: user.email,
subject: 'Password Reset',
html: `<p>Reset your password: <a href="${resetUrl}">${resetUrl}</a></p>`,
text: `Reset your password: ${resetUrl}`
});
}
}
module.exports = new EmailService();---
18. Pagination
// middleware/paginate.js
function paginate(model) {
return async (req, res, next) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
try {
const total = await model.countDocuments();
const data = await model.find()
.limit(limit)
.skip(skip)
.sort(req.query.sort || '-createdAt');
req.paginatedResults = {
data,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
hasNext: page * limit < total,
hasPrev: page > 1
}
};
next();
} catch (error) {
next(error);
}
};
}
module.exports = paginate;
// Usage
router.get('/users',
paginate(User),
(req, res) => {
res.json(req.paginatedResults);
}
);---
19. Search & Filtering
// controllers/searchController.js
exports.search = asyncHandler(async (req, res) => {
const {
q,
category,
minPrice,
maxPrice,
inStock,
sort = '-createdAt',
page = 1,
limit = 10
} = req.query;
// Build query
const query = {};
if (q) {
query.$or = [
{ name: { $regex: q, $options: 'i' } },
{ description: { $regex: q, $options: 'i' } }
];
}
if (category) {
query.category = category;
}
if (minPrice || maxPrice) {
query.price = {};
if (minPrice) query.price.$gte = parseFloat(minPrice);
if (maxPrice) query.price.$lte = parseFloat(maxPrice);
}
if (inStock !== undefined) {
query.inStock = inStock === 'true';
}
// Execute query
const skip = (parseInt(page) - 1) * parseInt(limit);
const [products, total] = await Promise.all([
Product.find(query)
.sort(sort)
.limit(parseInt(limit))
.skip(skip),
Product.countDocuments(query)
]);
res.json({
data: products,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
});---
20. Deployment
PM2 Ecosystem File
// ecosystem.config.js
module.exports = {
apps: [{
name: 'api',
script: './server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'development'
},
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: './logs/pm2-error.log',
out_file: './logs/pm2-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
merge_logs: true,
max_memory_restart: '1G',
autorestart: true,
watch: false,
ignore_watch: ['node_modules', 'logs'],
max_restarts: 10,
min_uptime: '10s'
}]
};Dockerfile
FROM node:16-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js
# Start application
CMD ["node", "server.js"]Docker Compose
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
restart: unless-stopped
mongo:
image: mongo:5
volumes:
- mongo-data:/data/db
restart: unless-stopped
redis:
image: redis:alpine
restart: unless-stopped
volumes:
mongo-data:---
Summary
These examples cover the essential patterns and use cases for Express.js development:
- Server setup and configuration
- Routing and route organization
- Middleware implementation
- Authentication and authorization
- Input validation
- Database integration
- File uploads
- Sessions and cookies
- CORS and security
- Rate limiting
- API versioning
- Error handling
- Logging
- Testing
- Real-time features
- Email services
- Pagination and search
- Deployment strategies
Each example is production-ready and follows Express.js best practices.
Express.js Development
A comprehensive skill for building production-ready web applications and REST APIs using Express.js.
Overview
Express.js is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. This skill covers everything from basic server setup to advanced patterns for authentication, validation, error handling, and deployment.
What You'll Learn
- Routing: HTTP methods, route parameters, query strings, router modules
- Middleware: Application-level, router-level, error-handling, built-in, and third-party middleware
- Request/Response: Working with request objects, sending responses, headers, cookies
- Error Handling: Synchronous and asynchronous error handling patterns
- Authentication: JWT-based authentication and authorization
- Validation: Input validation and sanitization
- Database Integration: MongoDB with Mongoose, SQL databases
- Testing: Unit and integration testing with Jest and Supertest
- Security: Helmet, CORS, rate limiting, input sanitization
- Performance: Compression, caching, optimization techniques
- Deployment: Production best practices and deployment strategies
Installation
Prerequisites
- Node.js (v14 or higher)
- npm or yarn package manager
Basic Setup
# Create a new project
mkdir my-express-app
cd my-express-app
# Initialize package.json
npm init -y
# Install Express
npm install express
# Install development dependencies
npm install --save-dev nodemonRecommended Packages
# Essential middleware
npm install cors helmet morgan compression
# Authentication
npm install jsonwebtoken bcryptjs
# Validation
npm install express-validator
# Environment variables
npm install dotenv
# Database (MongoDB)
npm install mongoose
# Database (PostgreSQL)
npm install pg sequelize
# Testing
npm install --save-dev jest supertest
# Security
npm install express-rate-limit express-mongo-sanitize xss-cleanQuick Start
1. Create Basic Server
Create server.js:
const express = require('express');
const app = express();
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.get('/', (req, res) => {
res.json({ message: 'Hello Express!' });
});
app.get('/api/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString()
});
});
// Start server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});2. Run the Server
# Run directly
node server.js
# Or with nodemon (auto-restart)
npx nodemon server.js3. Test the API
# Using curl
curl http://localhost:3000/
curl http://localhost:3000/api/health
# Using HTTPie
http localhost:3000/
http localhost:3000/api/healthProject Structure
Basic Structure
my-express-app/
├── src/
│ ├── controllers/
│ │ ├── authController.js
│ │ └── userController.js
│ ├── middleware/
│ │ ├── auth.js
│ │ └── validation.js
│ ├── models/
│ │ └── User.js
│ ├── routes/
│ │ ├── auth.js
│ │ └── users.js
│ ├── utils/
│ │ ├── errors.js
│ │ └── asyncHandler.js
│ └── app.js
├── tests/
│ ├── auth.test.js
│ └── users.test.js
├── .env
├── .gitignore
├── package.json
└── server.jsProduction Structure
my-express-app/
├── src/
│ ├── api/
│ │ ├── controllers/
│ │ ├── middleware/
│ │ ├── routes/
│ │ └── validators/
│ ├── config/
│ │ ├── database.js
│ │ ├── logger.js
│ │ └── environment.js
│ ├── models/
│ ├── services/
│ ├── utils/
│ └── app.js
├── tests/
│ ├── integration/
│ └── unit/
├── logs/
├── .env.example
├── .env
├── .gitignore
├── jest.config.js
├── package.json
└── server.jsCore Concepts
Routing
Routes define how your application responds to client requests at particular endpoints.
const express = require('express');
const router = express.Router();
// GET request
router.get('/users', (req, res) => {
res.json({ users: [] });
});
// POST request
router.post('/users', (req, res) => {
const { name, email } = req.body;
res.status(201).json({ message: 'User created' });
});
// Route parameters
router.get('/users/:id', (req, res) => {
const { id } = req.params;
res.json({ userId: id });
});
// Query strings
router.get('/search', (req, res) => {
const { q, limit = 10 } = req.query;
res.json({ query: q, limit });
});
module.exports = router;Middleware
Middleware functions have access to the request and response objects and can modify them or end the request-response cycle.
// Application-level middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
});
// Built-in middleware
app.use(express.json());
app.use(express.static('public'));
// Third-party middleware
const cors = require('cors');
app.use(cors());
// Custom middleware
function authenticate(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
// Verify token
next();
}
app.get('/protected', authenticate, (req, res) => {
res.json({ message: 'Protected data' });
});Error Handling
Express provides a built-in error handling mechanism using middleware with four arguments.
// Async error wrapper
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
// Route with error handling
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
const error = new Error('User not found');
error.status = 404;
throw error;
}
res.json({ user });
}));
// Error handling middleware (must be last)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
});Environment Setup
Environment Variables
Create .env file:
NODE_ENV=development
PORT=3000
DATABASE_URL=mongodb://localhost/myapp
JWT_SECRET=your-secret-key
JWT_EXPIRES_IN=7d
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001Load Environment Variables
require('dotenv').config();
const PORT = process.env.PORT || 3000;
const DB_URL = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;Environment-Specific Configuration
const config = {
development: {
port: 3000,
db: 'mongodb://localhost/myapp-dev',
logLevel: 'debug'
},
production: {
port: process.env.PORT,
db: process.env.DATABASE_URL,
logLevel: 'error'
}
};
const environment = process.env.NODE_ENV || 'development';
module.exports = config[environment];Common Patterns
REST API Endpoint Structure
GET /api/users - List all users
GET /api/users/:id - Get single user
POST /api/users - Create user
PUT /api/users/:id - Update user (full update)
PATCH /api/users/:id - Update user (partial update)
DELETE /api/users/:id - Delete userResponse Format
// Success response
{
"data": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
},
"message": "User retrieved successfully"
}
// List response with pagination
{
"data": [...],
"pagination": {
"page": 1,
"limit": 10,
"total": 100,
"pages": 10
}
}
// Error response
{
"error": {
"message": "User not found",
"code": "USER_NOT_FOUND"
}
}Status Codes
- 200 OK: Successful GET, PUT, PATCH
- 201 Created: Successful POST
- 204 No Content: Successful DELETE
- 400 Bad Request: Invalid request data
- 401 Unauthorized: Authentication required
- 403 Forbidden: Insufficient permissions
- 404 Not Found: Resource not found
- 422 Unprocessable Entity: Validation failed
- 500 Internal Server Error: Server error
Testing
Setup Testing Environment
// jest.config.js
module.exports = {
testEnvironment: 'node',
coveragePathIgnorePatterns: ['/node_modules/'],
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js']
};Basic Test Example
const request = require('supertest');
const app = require('../src/app');
describe('User API', () => {
describe('GET /api/users', () => {
it('should return all users', async () => {
const response = await request(app)
.get('/api/users')
.expect(200);
expect(response.body).toHaveProperty('data');
expect(Array.isArray(response.body.data)).toBe(true);
});
});
describe('POST /api/users', () => {
it('should create a new user', async () => {
const userData = {
name: 'Test User',
email: 'test@example.com'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect(201);
expect(response.body.data).toHaveProperty('id');
expect(response.body.data.email).toBe(userData.email);
});
});
});Deployment
Production Checklist
- [ ] Set NODE_ENV=production
- [ ] Use environment variables for sensitive data
- [ ] Enable security middleware (helmet, CORS)
- [ ] Implement rate limiting
- [ ] Set up logging
- [ ] Configure error handling
- [ ] Use compression
- [ ] Set up monitoring
- [ ] Configure SSL/TLS
- [ ] Set up database connection pooling
PM2 Deployment
# Install PM2
npm install -g pm2
# Start application
pm2 start server.js --name "my-app"
# Start with environment
pm2 start server.js --name "my-app" --env production
# Monitor
pm2 monit
# View logs
pm2 logs
# Auto-restart on file changes
pm2 start server.js --watchDocker Deployment
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]Resources
Official Documentation
Recommended Middleware
- helmet: Security headers
- cors: Cross-Origin Resource Sharing
- morgan: HTTP request logger
- compression: Response compression
- express-rate-limit: Rate limiting
- express-validator: Request validation
- cookie-parser: Cookie parsing
- multer: File upload handling
Related Technologies
- MongoDB + Mongoose: NoSQL database
- PostgreSQL + Sequelize: SQL database
- Redis: Caching
- Passport.js: Authentication strategies
- Socket.io: Real-time communication
- GraphQL: Alternative to REST
Next Steps
1. Read through SKILL.md for comprehensive concepts and patterns 2. Explore EXAMPLES.md for practical implementations 3. Build a simple REST API following the examples 4. Implement authentication and authorization 5. Add validation and error handling 6. Write tests for your API 7. Deploy to production
Support
For issues or questions:
- Express.js GitHub: https://github.com/expressjs/express
- Stack Overflow: Use tag
express - Express.js Gitter: https://gitter.im/expressjs/express
License
This skill documentation is provided as-is for educational purposes.
Related skills
FAQ
What does expressjs-development help build?
expressjs-development helps build and extend Express.js HTTP APIs with routing, middleware, validation, error handling, and production-ready server structure so Node.js backends follow consistent patterns across endpoints.
When should you use expressjs-development?
expressjs-development fits new Express service scaffolding or refactors where routes, middleware order, validation, and error responses must be standardized before shipping a Node.js API.