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:
javascript
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:
javascript
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:
javascript
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:
javascript
// 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:
javascript
// 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:
javascript
// 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:
javascript
// 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:
javascript
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:
javascript
// 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:
javascript
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:
javascript
// 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:
javascript
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:
javascript
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:
javascript
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:
javascript
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:
javascript
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:
javascript
// 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:
javascript
// 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