---
name: express-nodejs-expert
description: Expert knowledge of Express.js and Node.js for building production-ready web applications and APIs. Covers middleware patterns, routing, async/await error handling, security, performance optimization, proxy patterns, static file serving, and production deployment. Use when working with server.js, adding routes, implementing middleware, debugging Express issues, or optimizing API endpoints.
---
# Express.js & Node.js Expert
This skill provides comprehensive expert knowledge of Express.js web framework and Node.js runtime for building robust, secure, and performant web applications and API servers.
## Express Application Structure
### Basic Application Setup
**Minimal Express app**:
```javascript
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
```
**Production-ready structure**:
```javascript
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
const app = express();
const port = process.env.PORT || 3000;
// Middleware
app.use(helmet()); // Security headers
app.use(cors()); // CORS support
app.use(morgan('combined')); // Logging
app.use(express.json()); // Parse JSON bodies
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies
app.use(express.static('public')); // Serve static files
// Routes
app.use('/api', require('./routes/api'));
// Error handling
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 })
}
});
});
// Start server
const server = app.listen(port, () => {
console.log(`Server running in ${process.env.NODE_ENV || 'development'} mode on port ${port}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(() => {
console.log('HTTP server closed');
});
});
module.exports = app;
```
## Middleware Patterns
### Middleware Execution Order
**Order matters**:
```javascript
// 1. Security middleware (first)
app.use(helmet());
// 2. Logging
app.use(morgan('combined'));
// 3. Body parsing
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 4. CORS
app.use(cors());
// 5. Static files
app.use(express.static('public'));
// 6. Custom middleware
app.use(customMiddleware);
// 7. Routes
app.use('/api', apiRoutes);
// 8. 404 handler (after all routes)
app.use((req, res) => {
res.status(404).json({ error: 'Not Found' });
});
// 9. Error handler (last)
app.use((err, req, res, next) => {
// Error handling
});
```
### Built-in Middleware
**express.json()** - Parse JSON request bodies:
```javascript
app.use(express.json({ limit: '10mb' })); // Set size limit
// Now req.body contains parsed JSON
app.post('/api/data', (req, res) => {
console.log(req.body); // { key: 'value' }
res.json({ received: req.body });
});
```
**express.urlencoded()** - Parse URL-encoded bodies:
```javascript
app.use(express.urlencoded({ extended: true }));
// Handles form submissions
app.post('/form', (req, res) => {
console.log(req.body); // { name: 'John', email: 'john@example.com' }
});
```
**express.static()** - Serve static files:
```javascript
// Serve files from 'public' directory
app.use(express.static('public'));
// With options
app.use(express.static('public', {
maxAge: '1d', // Cache for 1 day
etag: true,
lastModified: true,
index: 'index.html'
}));
// Multiple static directories
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
```
### Custom Middleware
**Simple middleware**:
```javascript
// Logging middleware
const logger = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next(); // MUST call next() to continue
};
app.use(logger);
```
**Async middleware**:
```javascript
const asyncMiddleware = async (req, res, next) => {
try {
// Async operations
const data = await fetchData();
req.data = data;
next();
} catch (error) {
next(error); // Pass errors to error handler
}
};
app.use(asyncMiddleware);
```
**Conditional middleware**:
```javascript
const devOnly = (req, res, next) => {
if (process.env.NODE_ENV === 'development') {
return next();
}
res.status(403).json({ error: 'Development only' });
};
app.get('/debug', devOnly, (req, res) => {
res.json({ debug: 'info' });
});
```
**Error handling middleware** (must have 4 parameters):
```javascript
app.use((err, req, res, next) => {
console.error(err.stack);
// Handle specific error types
if (err.name === 'ValidationError') {
return res.status(400).json({ error: err.message });
}
if (err.name === 'UnauthorizedError') {
return res.status(401).json({ error: 'Unauthorized' });
}
// Generic error
res.status(err.status || 500).json({
error: process.env.NODE_ENV === 'production'
? 'Internal Server Error'
: err.message
});
});
```
## Routing Best Practices
### Route Organization
**Inline routes** (small apps):
```javascript
app.get('/', (req, res) => res.send('Home'));
app.get('/about', (req, res) => res.send('About'));
app.post('/api/users', (req, res) => {/* ... */});
```
**Router modules** (recommended):
```javascript
// routes/api.js
const express = require('express');
const router = express.Router();
router.get('/users', (req, res) => {
res.json({ users: [] });
});
router.post('/users', (req, res) => {
res.json({ created: true });
});
module.exports = router;
// server.js
const apiRoutes = require('./routes/api');
app.use('/api', apiRoutes);
```
**Grouped routes by resource**:
```javascript
// routes/users.js
const router = express.Router();
router.get('/', getAllUsers);
router.get('/:id', getUser);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);
module.exports = router;
// server.js
app.use('/api/users', require('./routes/users'));
```
### Route Parameters
**Path parameters**:
```javascript
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.json({ userId });
});
// Multiple parameters
app.get('/users/:userId/posts/:postId', (req, res) => {
const { userId, postId } = req.params;
res.json({ userId, postId });
});
```
**Query parameters**:
```javascript
app.get('/search', (req, res) => {
const { q, page, limit } = req.query;
// /search?q=test&page=2&limit=10
res.json({ query: q, page, limit });
});
```
**Route parameter validation**:
```javascript
// Middleware to validate ID
const validateId = (req, res, next) => {
const id = parseInt(req.params.id);
if (isNaN(id) || id < 1) {
return res.status(400).json({ error: 'Invalid ID' });
}
req.params.id = id; // Convert to number
next();
};
app.get('/users/:id', validateId, (req, res) => {
// req.params.id is now a number
});
```
### HTTP Methods
**RESTful API routes**:
```javascript
const router = express.Router();
// GET - Retrieve resources
router.get('/items', (req, res) => {
res.json({ items: [] });
});
router.get('/items/:id', (req, res) => {
res.json({ item: {} });
});
// POST - Create resource
router.post('/items', (req, res) => {
const newItem = req.body;
res.status(201).json({ created: newItem });
});
// PUT - Update entire resource
router.put('/items/:id', (req, res) => {
const updated = req.body;
res.json({ updated });
});
// PATCH - Partial update
router.patch('/items/:id', (req, res) => {
const updates = req.body;
res.json({ updated: updates });
});
// DELETE - Remove resource
router.delete('/items/:id', (req, res) => {
res.status(204).send(); // No content
});
```
## Async/Await Error Handling
### The Problem
**Without proper handling**:
```javascript
// BAD - Unhandled promise rejection
app.get('/users', async (req, res) => {
const users = await fetchUsers(); // If this throws, app crashes
res.json(users);
});
```
### Solutions
**Option 1: Try-catch in every route**:
```javascript
app.get('/users', async (req, res) => {
try {
const users = await fetchUsers();
res.json(users);
} catch (error) {
console.error(error);
res.status(500).json({ error: error.message });
}
});
```
**Option 2: Async wrapper utility** (recommended):
```javascript
// utils/asyncHandler.js
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
module.exports = asyncHandler;
// Usage
const asyncHandler = require('./utils/asyncHandler');
app.get('/users', asyncHandler(async (req, res) => {
const users = await fetchUsers(); // Errors automatically caught
res.json(users);
}));
```
**Option 3: express-async-errors package**:
```javascript
// Install: npm install express-async-errors
require('express-async-errors'); // At the top of your app
// Now async errors are automatically caught
app.get('/users', async (req, res) => {
const users = await fetchUsers();
res.json(users);
});
// Error handler catches async errors
app.use((err, req, res, next) => {
res.status(500).json({ error: err.message });
});
```
### Custom Error Classes
```javascript
// errors/AppError.js
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
// Usage
const AppError = require('./errors/AppError');
app.get('/users/:id', async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) {
return next(new AppError('User not found', 404));
}
res.json(user);
});
// Error handler
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
res.status(err.statusCode).json({
status: err.status,
message: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});
```
## Request/Response Patterns
### Request Object
**Common properties**:
```javascript
app.post('/api/data', (req, res) => {
// URL parameters
const { id } = req.params; // /api/data/:id
// Query string
const { page, limit } = req.query; // /api/data?page=1&limit=10
// Request body (requires express.json())
const data = req.body;
// Headers
const contentType = req.get('Content-Type');
const auth = req.headers.authorization;
// Request info
const method = req.method; // POST
const path = req.path; // /api/data
const url = req.url; // /api/data?page=1
const protocol = req.protocol; // http or https
const ip = req.ip; // Client IP
// Cookies (requires cookie-parser)
const sessionId = req.cookies.sessionId;
});
```
### Response Methods
**Send responses**:
```javascript
// Send JSON
res.json({ message: 'Success' });
res.status(201).json({ created: true });
// Send text
res.send('Plain text');
// Send HTML
res.send('
HTML
');
// Send file
res.sendFile('/path/to/file.pdf');
// Download file
res.download('/path/to/file.pdf', 'filename.pdf');
// Redirect
res.redirect('/new-url');
res.redirect(301, '/permanent-redirect');
// Set status
res.status(404).json({ error: 'Not Found' });
res.sendStatus(204); // No Content
```
**Set headers**:
```javascript
res.set('Content-Type', 'application/json');
res.set({
'Content-Type': 'application/json',
'X-Custom-Header': 'value'
});
// Set cookies
res.cookie('sessionId', '12345', {
maxAge: 900000,
httpOnly: true,
secure: true,
sameSite: 'strict'
});
// Clear cookies
res.clearCookie('sessionId');
```
### Response Patterns
**Success responses**:
```javascript
// 200 OK - General success
res.json({ data: items });
// 201 Created - Resource created
res.status(201).json({ id: newId, created: true });
// 204 No Content - Success with no response body
res.status(204).send();
```
**Error responses**:
```javascript
// 400 Bad Request - Invalid input
res.status(400).json({ error: 'Invalid email format' });
// 401 Unauthorized - Authentication required
res.status(401).json({ error: 'Authentication required' });
// 403 Forbidden - Insufficient permissions
res.status(403).json({ error: 'Access denied' });
// 404 Not Found - Resource doesn't exist
res.status(404).json({ error: 'User not found' });
// 409 Conflict - Resource conflict
res.status(409).json({ error: 'Email already exists' });
// 422 Unprocessable Entity - Validation error
res.status(422).json({
error: 'Validation failed',
details: validationErrors
});
// 500 Internal Server Error - Server error
res.status(500).json({ error: 'Internal server error' });
```
## Proxy Patterns with Axios
### Basic Proxy
```javascript
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.post('/api/proxy', async (req, res) => {
try {
const response = await axios.post(
'https://external-api.com/endpoint',
req.body,
{ headers: { 'Content-Type': 'application/json' } }
);
res.json(response.data);
} catch (error) {
console.error('Proxy error:', error.message);
res.status(error.response?.status || 500).json({
error: 'Proxy request failed',
details: error.response?.data || error.message
});
}
});
```
### Advanced Proxy with Headers
```javascript
app.post('/api/proxy', async (req, res) => {
try {
// Forward headers from client
const headers = {
'Content-Type': 'application/json',
'Authorization': req.headers.authorization,
'User-Agent': req.headers['user-agent']
};
const response = await axios.post(
'https://external-api.com/endpoint',
req.body,
{
headers,
timeout: 5000, // 5 second timeout
validateStatus: (status) => status < 500 // Don't throw on 4xx
}
);
// Forward response headers
res.set('X-Response-Time', response.headers['x-response-time']);
res.status(response.status).json(response.data);
} catch (error) {
if (error.code === 'ECONNABORTED') {
return res.status(504).json({ error: 'Gateway timeout' });
}
res.status(error.response?.status || 500).json({
error: 'Proxy request failed',
originalError: error.response?.data || null
});
}
});
```
### Proxy with Retry Logic
```javascript
const axios = require('axios');
const axiosRetry = require('axios-retry');
// Configure axios with retry
axiosRetry(axios, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: (error) => {
return axiosRetry.isNetworkOrIdempotentRequestError(error)
|| error.response?.status === 429; // Retry on rate limit
}
});
app.post('/api/proxy', async (req, res) => {
try {
const response = await axios.post(
'https://external-api.com/endpoint',
req.body,
{
headers: { 'Content-Type': 'application/json' },
'axios-retry': {
retries: 3
}
}
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Request failed after retries',
details: error.response?.data
});
}
});
```
### Request Validation Before Proxying
```javascript
const Joi = require('joi');
const requestSchema = Joi.object({
keyword: Joi.string().max(100),
startDate: Joi.date().iso(),
endDate: Joi.date().iso().min(Joi.ref('startDate'))
});
app.post('/api/search', async (req, res) => {
// Validate request body
const { error, value } = requestSchema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.details
});
}
try {
const response = await axios.post(
'https://external-api.com/search',
value, // Use validated data
{ headers: { 'Content-Type': 'application/json' } }
);
res.json(response.data);
} catch (error) {
res.status(error.response?.status || 500).json({
error: 'Search request failed',
details: error.response?.data
});
}
});
```
## Security Best Practices
### Helmet - Security Headers
```javascript
const helmet = require('helmet');
// Basic usage
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
```javascript
const cors = require('cors');
// Allow all origins (development only)
app.use(cors());
// Production configuration
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://myapp.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://myapp.com', 'https://app.example.com'];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));
```
### Rate Limiting
```javascript
const rateLimit = require('express-rate-limit');
// Global rate limit
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many requests, please try again later.',
standardHeaders: true,
legacyHeaders: false
});
app.use(limiter);
// Route-specific rate limit
const strictLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Too many login attempts, please try again later.'
});
app.post('/api/login', strictLimiter, (req, res) => {
// Login logic
});
```
### Input Validation and Sanitization
```javascript
const { body, validationResult } = require('express-validator');
app.post('/api/users',
// Validation middleware
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).trim(),
body('name').trim().escape(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Proceed with validated data
const { email, password, name } = req.body;
res.json({ created: true });
}
);
```
### Prevent Parameter Pollution
```javascript
const hpp = require('hpp');
// Prevent query parameter pollution
app.use(hpp());
// Whitelist certain parameters that can be arrays
app.use(hpp({
whitelist: ['tags', 'categories']
}));
```
## Performance Optimization
### Compression
```javascript
const compression = require('compression');
app.use(compression({
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
},
level: 6 // Compression level (0-9)
}));
```
### Caching Headers
```javascript
// Static files with caching
app.use(express.static('public', {
maxAge: '1d',
etag: true,
lastModified: true
}));
// API responses with cache control
app.get('/api/data', (req, res) => {
res.set('Cache-Control', 'public, max-age=300'); // 5 minutes
res.json({ data: [] });
});
// No cache for dynamic data
app.get('/api/user/profile', (req, res) => {
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private');
res.json({ user: {} });
});
```
### Response Time Tracking
```javascript
const responseTime = require('response-time');
app.use(responseTime((req, res, time) => {
console.log(`${req.method} ${req.url} - ${time.toFixed(2)}ms`);
}));
// Or send as header
app.use(responseTime());
```
### Connection Pooling (for databases)
```javascript
// Example with PostgreSQL
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
max: 20, // Maximum pool size
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000
});
app.get('/api/users', async (req, res) => {
const client = await pool.connect();
try {
const result = await client.query('SELECT * FROM users');
res.json(result.rows);
} finally {
client.release();
}
});
```
## Production Configuration
### Environment-based Configuration
```javascript
const express = require('express');
const app = express();
const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';
// Development-only middleware
if (isDevelopment) {
const morgan = require('morgan');
app.use(morgan('dev'));
}
// Production-only middleware
if (isProduction) {
app.use(require('compression')());
app.use(require('helmet')());
}
// Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: isProduction ? 'Internal Server Error' : err.message,
...(isDevelopment && { stack: err.stack })
});
});
```
### Graceful Shutdown
```javascript
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
const server = app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
// Handle shutdown signals
const gracefulShutdown = (signal) => {
console.log(`\n${signal} signal received: closing HTTP server`);
server.close(() => {
console.log('HTTP server closed');
// Close database connections, cleanup resources
// db.close();
process.exit(0);
});
// Force close after 10 seconds
setTimeout(() => {
console.error('Forcing shutdown');
process.exit(1);
}, 10000);
};
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
gracefulShutdown('uncaughtException');
});
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
gracefulShutdown('unhandledRejection');
});
```
### Logging
```javascript
const winston = require('winston');
const morgan = require('morgan');
// Winston logger
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
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 for HTTP logging
app.use(morgan('combined', {
stream: {
write: (message) => logger.info(message.trim())
}
}));
// Use logger in routes
app.get('/api/data', async (req, res) => {
try {
const data = await fetchData();
logger.info('Data fetched successfully');
res.json(data);
} catch (error) {
logger.error('Error fetching data:', { error: error.message, stack: error.stack });
res.status(500).json({ error: 'Internal server error' });
}
});
```
## Testing Express Applications
### Setup with Jest and Supertest
```javascript
// Install: npm install --save-dev jest supertest
// server.test.js
const request = require('supertest');
const app = require('./server');
describe('GET /', () => {
it('responds with 200 status', async () => {
const response = await request(app).get('/');
expect(response.status).toBe(200);
});
it('responds with JSON', async () => {
const response = await request(app).get('/api/users');
expect(response.headers['content-type']).toMatch(/json/);
});
});
describe('POST /api/users', () => {
it('creates a user with valid data', async () => {
const userData = { name: 'John', email: 'john@example.com' };
const response = await request(app)
.post('/api/users')
.send(userData)
.set('Content-Type', 'application/json');
expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');
});
it('rejects invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'John', email: 'invalid' });
expect(response.status).toBe(400);
});
});
```
### Mocking External APIs
```javascript
const axios = require('axios');
jest.mock('axios');
describe('POST /api/proxy', () => {
it('proxies request successfully', async () => {
const mockData = { result: 'success' };
axios.post.mockResolvedValue({ data: mockData });
const response = await request(app)
.post('/api/proxy')
.send({ query: 'test' });
expect(response.status).toBe(200);
expect(response.body).toEqual(mockData);
expect(axios.post).toHaveBeenCalledWith(
expect.any(String),
{ query: 'test' },
expect.any(Object)
);
});
it('handles proxy errors', async () => {
axios.post.mockRejectedValue(new Error('Network error'));
const response = await request(app)
.post('/api/proxy')
.send({ query: 'test' });
expect(response.status).toBe(500);
expect(response.body).toHaveProperty('error');
});
});
```
## Common Express Issues
### Headers Already Sent
**Problem**:
```javascript
// BAD - sends headers twice
app.get('/data', (req, res) => {
res.json({ data: [] });
res.status(200).send(); // Error: headers already sent
});
```
**Solution**:
```javascript
// GOOD - single response
app.get('/data', (req, res) => {
return res.json({ data: [] }); // Use return to prevent further execution
});
// Or use else
app.get('/data', (req, res) => {
if (error) {
return res.status(500).json({ error: 'Failed' });
} else {
return res.json({ data: [] });
}
});
```
### Middleware Not Running
**Problem**: Middleware defined after routes
```javascript
// BAD - middleware defined after route
app.get('/api/data', (req, res) => {
res.json({ data: req.user }); // req.user is undefined
});
app.use(authMiddleware); // Too late!
```
**Solution**: Define middleware before routes
```javascript
// GOOD - middleware before routes
app.use(authMiddleware);
app.get('/api/data', (req, res) => {
res.json({ data: req.user }); // req.user exists
});
```
### Forgot to call next()
**Problem**:
```javascript
// BAD - middleware doesn't call next()
app.use((req, res) => {
console.log('Request received');
// Request hangs here!
});
```
**Solution**:
```javascript
// GOOD - always call next()
app.use((req, res, next) => {
console.log('Request received');
next(); // Continue to next middleware
});
```
### CORS Errors
**Problem**: Missing CORS headers
```javascript
// Frontend gets CORS error
```
**Solution**:
```javascript
const cors = require('cors');
app.use(cors());
// Or manual headers
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
```
### Body Parser Not Working
**Problem**: req.body is undefined
```javascript
app.post('/api/data', (req, res) => {
console.log(req.body); // undefined
});
```
**Solution**: Add body parser middleware
```javascript
app.use(express.json()); // Parse JSON
app.use(express.urlencoded({ extended: true })); // Parse URL-encoded
app.post('/api/data', (req, res) => {
console.log(req.body); // Now it works
});
```
## Best Practices Summary
### Application Structure
1. Use environment variables for configuration
2. Implement graceful shutdown
3. Use middleware in correct order
4. Separate routes into modules
5. Implement proper error handling
### Security
1. Always use helmet for security headers
2. Configure CORS appropriately
3. Implement rate limiting
4. Validate and sanitize all inputs
5. Never expose sensitive errors in production
6. Use HTTPS in production
7. Keep dependencies updated
### Performance
1. Use compression middleware
2. Implement caching where appropriate
3. Use connection pooling for databases
4. Minimize middleware stack
5. Use async/await instead of callbacks
### Error Handling
1. Use async error wrapper or express-async-errors
2. Create custom error classes
3. Centralize error handling middleware
4. Log errors appropriately
5. Never crash on unhandled errors
### Code Quality
1. Use consistent naming conventions
2. Keep route handlers small and focused
3. Extract business logic into separate modules
4. Write tests for routes and middleware
5. Use TypeScript for larger applications
### Production Readiness
1. Set NODE_ENV=production
2. Implement logging (Winston, Morgan)
3. Use process managers (PM2, Docker)
4. Monitor application health
5. Implement graceful shutdown
6. Handle uncaught exceptions
## Resources
- Express.js Documentation: https://expressjs.com/
- Node.js Best Practices: https://github.com/goldbergyoni/nodebestpractices
- Express Security: https://expressjs.com/en/advanced/best-practice-security.html
- Helmet Documentation: https://helmetjs.github.io/
- Axios Documentation: https://axios-http.com/docs/intro