Middleware
Middleware in Balda.js are functions that have access to the request object, response object, and the next middleware function in the application's request-response cycle. They can execute code, modify the request and response objects, end the request-response cycle, and call the next middleware function.
Basic Middleware
Function Signature
type Middleware = (
req: Request,
res: Response,
next: NextFunction
) => void | Promise<void>;
Simple Middleware Example
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
next();
};
const auth = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.unauthorized({ error: 'Authentication required' });
}
// Verify token logic here
req.user = { id: 1, name: 'John' };
next();
};
Middleware Application
Global Middleware
Apply middleware to all routes:
import { Server } from 'balda-js';
const server = new Server({ port: 3000 });
// Apply to all routes
server.use(logger);
server.use(cors);
// Multiple middleware
server.use(
(req, res, next) => {
req.startTime = Date.now();
next();
},
(req, res, next) => {
res.on('finish', () => {
const duration = Date.now() - req.startTime;
console.log(`${req.method} ${req.url} - ${duration}ms`);
});
next();
}
);
Controller-Level Middleware
Apply middleware to all routes in a controller:
import { controller, get, middleware } from 'balda-js';
@controller('/users')
@middleware(authMiddleware)
export class UsersController {
@get('/')
async getAllUsers(req, res) {
// This route will use authMiddleware
res.json({ users: [] });
}
@get('/public')
@middleware(publicMiddleware) // Override controller middleware
async getPublicUsers(req, res) {
// This route will use publicMiddleware instead
res.json({ users: [] });
}
}
Route-Level Middleware
Apply middleware to specific routes:
@get('/admin/users', { middleware: [authMiddleware, adminMiddleware] })
async getAdminUsers(req, res) {
res.json({ users: [] });
}
Common Middleware Patterns
Authentication Middleware
const authMiddleware = async (req, res, next) => {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.unauthorized({ error: 'Authentication token required' });
}
try {
// Verify JWT token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.unauthorized({ error: 'Invalid authentication token' });
}
};
Role-Based Authorization
const requireRole = (role) => {
return (req, res, next) => {
if (!req.user) {
return res.unauthorized({ error: 'Authentication required' });
}
if (req.user.role !== role) {
return res.forbidden({ error: 'Insufficient permissions' });
}
next();
};
};
// Usage
@get('/admin', { middleware: [authMiddleware, requireRole('admin')] })
async getAdminPanel(req, res) {
res.json({ admin: true });
}
Request Logging
const requestLogger = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`${req.method} ${req.url} ${res.statusCode} - ${duration}ms`);
});
next();
};
Error Handling Middleware
const errorHandler = (req, res, next, error) => {
console.error('Error:', error);
if (error.name === 'ValidationError') {
return res.badRequest({ error: error.message });
}
if (error.name === 'UnauthorizedError') {
return res.unauthorized({ error: 'Authentication required' });
}
res.internalServerError({ error: 'Internal server error' });
};
server.setErrorHandler(errorHandler);
CORS Middleware
const corsMiddleware = (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
next();
};
Rate Limiting
const rateLimit = (windowMs = 15 * 60 * 1000, max = 100) => {
const requests = new Map();
return (req, res, next) => {
const ip = req.ip;
const now = Date.now();
if (!requests.has(ip)) {
requests.set(ip, { count: 0, resetTime: now + windowMs });
}
const record = requests.get(ip);
if (now > record.resetTime) {
record.count = 0;
record.resetTime = now + windowMs;
}
if (record.count >= max) {
return res.tooManyRequests({ error: 'Too many requests' });
}
record.count++;
next();
};
};
Middleware Order
The order of middleware matters. Middleware are executed in the order they are applied:
// 1. Global middleware (applied first)
server.use(logger);
server.use(cors);
// 2. Controller middleware
@controller('/users')
@middleware(authMiddleware) // Applied to all routes in controller
// 3. Route middleware (applied last)
@get('/admin', { middleware: [adminMiddleware] })
async getAdmin(req, res) {
// Execution order: logger -> cors -> authMiddleware -> adminMiddleware -> handler
}
Async Middleware
Middleware can be async functions:
const asyncAuthMiddleware = async (req, res, next) => {
try {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.unauthorized({ error: 'Authentication required' });
}
// Async operation
const user = await verifyToken(token);
req.user = user;
next();
} catch (error) {
return res.unauthorized({ error: 'Invalid token' });
}
};
Conditional Middleware
Apply middleware conditionally:
const conditionalAuth = (req, res, next) => {
// Skip auth for public routes
if (req.path.startsWith('/public')) {
return next();
}
// Apply auth for protected routes
const token = req.headers.authorization;
if (!token) {
return res.unauthorized({ error: 'Authentication required' });
}
req.user = { id: 1 };
next();
};