Middleware
Kempo Server includes a powerful middleware system that allows you to add functionality like authentication, logging, CORS, compression, and more.
How Middleware Works
Middleware functions run before your route handlers and can:
- Modify request and response objects
- Add new properties to requests (authentication, user data, etc.)
- Handle requests entirely (authentication checks, rate limiting)
- Log requests and responses
- Add security headers
- Compress responses
Built-in Middleware
Kempo Server includes several built-in middleware options that you can enable and configure:
CORS (Cross-Origin Resource Sharing)
Enable Cross-Origin Resource Sharing to allow requests from different domains:
{
"middleware": {
"cors": {
"enabled": true,
"origin": "*",
"methods": ["GET", "POST", "PUT", "DELETE"],
"headers": ["Content-Type", "Authorization"],
"credentials": true
}
}
}
CORS Configuration Options
origin- Allowed origins ("*"for all, or specific domains)methods- Allowed HTTP methodsheaders- Allowed request headerscredentials- Allow credentials (cookies, authorization headers)maxAge- How long browsers can cache preflight responses
Compression
Automatically compress responses with gzip to reduce bandwidth:
{
"middleware": {
"compression": {
"enabled": true,
"threshold": 1024,
"level": 6
}
}
}
Compression Configuration Options
threshold- Minimum size in bytes before compression is appliedlevel- Compression level (1-9, where 9 is best compression)filter- Function to determine which responses to compress
Rate Limiting
Limit the number of requests per client to prevent abuse:
{
"middleware": {
"rateLimit": {
"enabled": true,
"maxRequests": 100,
"windowMs": 60000,
"message": "Too many requests, please try again later."
}
}
}
Rate Limiting Configuration Options
maxRequests- Maximum number of requests per windowwindowMs- Time window in millisecondsmessage- Error message to send when limit is exceededstatusCode- HTTP status code to return (default: 429)
Security Headers
Add security headers to responses to protect against common attacks:
{
"middleware": {
"security": {
"enabled": true,
"headers": {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
"Referrer-Policy": "strict-origin-when-cross-origin"
}
}
}
}
Common Security Headers
X-Content-Type-Options- Prevents MIME type sniffingX-Frame-Options- Prevents clickjacking attacksX-XSS-Protection- Enables XSS filteringStrict-Transport-Security- Enforces HTTPS connectionsContent-Security-Policy- Controls resource loadingReferrer-Policy- Controls referrer information
Custom Middleware
Create your own middleware by writing JavaScript files and referencing them in your config:
{
"middleware": {
"custom": [
"./middleware/auth.js",
"./middleware/logging.js",
"./middleware/analytics.js"
]
}
}
Custom Middleware Structure
A custom middleware file must export a default function that returns a middleware function:
// middleware/example.js
export default (config) => {
return async (req, res, next) => {
// Your middleware logic here
await next();
};
};
Middleware Function Parameters
config- Configuration object (can be used for middleware settings)req- Request object (can be modified)res- Response object (can be modified)next- Function to call the next middleware or route handler
Middleware Examples
Authentication Middleware
// middleware/auth.js
export default (config) => {
return async (req, res, next) => {
const token = req.headers.authorization;
if (token) {
try {
const user = await verifyToken(token.replace('Bearer ', ''));
req.user = user;
req.permissions = await getUserPermissions(user.id);
req.hasPermission = (permission) => req.permissions.includes(permission);
} catch (error) {
// Token is invalid, but don't block the request
req.user = null;
}
} else {
req.user = null;
}
await next();
};
};
// Helper functions (you would implement these)
async function verifyToken(token) {
// Verify JWT token or session token
// Return user object if valid, throw error if invalid
}
async function getUserPermissions(userId) {
// Fetch user permissions from database
// Return array of permission strings
}
Logging Middleware
// middleware/logging.js
export default (config) => {
return async (req, res, next) => {
const startTime = Date.now();
const { method, path, headers } = req;
// Log request
console.log(`[${new Date().toISOString()}] ${method} ${path}`);
// Store original response methods
const originalSend = res.send;
const originalJson = res.json;
const originalHtml = res.html;
// Override response methods to log response
res.send = function(data) {
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] ${method} ${path} - ${res.statusCode || 200} - ${duration}ms`);
return originalSend.call(this, data);
};
res.json = function(data) {
const duration = Date.now() - startTime;
console.log(`[${new Date().toISOString()}] ${method} ${path} - ${res.statusCode || 200} - ${duration}ms`);
return originalJson.call(this, data);
};
await next();
};
};
Analytics Middleware
// middleware/analytics.js
export default (config) => {
return async (req, res, next) => {
const userAgent = req.get('user-agent') || 'Unknown';
const referer = req.get('referer') || 'Direct';
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
// Track page view
await trackPageView({
path: req.path,
method: req.method,
userAgent,
referer,
ip,
timestamp: new Date()
});
await next();
};
};
async function trackPageView(data) {
// Send analytics data to your tracking service
// or save to database
}
Request Validation Middleware
// middleware/validation.js
export default (config) => {
return async (req, res, next) => {
// Add validation helpers to request object
req.validate = {
email: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(value);
},
required: (value) => {
return value !== undefined && value !== null && value !== '';
},
minLength: (value, min) => {
return typeof value === 'string' && value.length >= min;
},
maxLength: (value, max) => {
return typeof value === 'string' && value.length <= max;
}
};
await next();
};
};
Using Enhanced Requests in Routes
Once middleware has enhanced your request object, you can use the added properties in your route handlers:
Using Authentication Data
// api/user/profile/GET.js
export default async function(req, res) {
// Check if user is authenticated (added by auth middleware)
if (!req.user) {
return res.status(401).json({ error: 'Authentication required' });
}
// Check permissions (added by auth middleware)
if (!req.hasPermission('user:read')) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
const profile = await getUserProfile(req.user.id);
res.json(profile);
}
Using Validation Helpers
// api/user/POST.js
export default async function(req, res) {
const { name, email, password } = await req.json();
const errors = [];
// Use validation helpers added by validation middleware
if (!req.validate.required(name)) {
errors.push('Name is required');
}
if (!req.validate.required(email)) {
errors.push('Email is required');
} else if (!req.validate.email(email)) {
errors.push('Invalid email format');
}
if (!req.validate.minLength(password, 8)) {
errors.push('Password must be at least 8 characters');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
// Create user...
const user = await createUser({ name, email, password });
res.status(201).json(user);
}
Middleware Best Practices
Order Matters
Middleware runs in the order specified in your configuration. Common ordering:
- Security headers (should run first)
- CORS (before authentication)
- Rate limiting (before heavy processing)
- Authentication (before authorization)
- Logging (can be early or late)
- Compression (should run last)
Error Handling
Always handle errors gracefully in custom middleware:
export default (config) => {
return async (req, res, next) => {
try {
// Your middleware logic
await next();
} catch (error) {
console.error('Middleware error:', error);
// Don't break the request chain
await next();
}
};
};
Performance Considerations
- Keep middleware lightweight - avoid heavy processing
- Use async/await properly to avoid blocking
- Cache expensive operations when possible
- Consider the performance impact of middleware order