Home

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:

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

Compression

Automatically compress responses with gzip to reduce bandwidth:

{
"middleware": {
"compression": {
"enabled": true,
"threshold": 1024,
"level": 6
}
}
}

Compression Configuration Options

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

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

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

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:

  1. Security headers (should run first)
  2. CORS (before authentication)
  3. Rate limiting (before heavy processing)
  4. Authentication (before authorization)
  5. Logging (can be early or late)
  6. 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