Skip to content
Plurafy

Middleware Architecture and Connect

Understanding middleware pattern for request processing

Your server needs to log requests, authenticate users, parse JSON, handle errors, and process responses. How do you compose all these concerns without creating a monolithic request handler?

Middleware is a function that processes requests/responses sequentially. Express and other frameworks are built on this pattern, allowing you to compose concerns as pluggable modules.

const http = require('http');
// Middleware function
function middleware(req, res, next) {
// Do something
console.log(`${req.method} ${req.url}`);
// Call next middleware
next();
}
// Simple middleware stack
function createApp(middlewares = []) {
return http.createServer(async (req, res) => {
let index = 0;
function next() {
if (index >= middlewares.length) {
if (!res.headersSent) {
res.writeHead(404);
res.end('Not Found');
}
return;
}
const middleware = middlewares[index++];
middleware(req, res, next);
}
next();
});
}
// Using middleware
const app = createApp([
// Logging middleware
(req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
},
// Authentication middleware
(req, res, next) => {
if (req.headers.authorization) {
req.user = { authenticated: true };
next();
} else {
res.writeHead(401);
res.end('Unauthorized');
}
},
// Main handler
(req, res, next) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ message: 'OK', user: req.user }));
}
]);
app.listen(3000);
const express = require('express');
const app = express();
// Built-in middleware
app.use(express.json()); // Parse JSON
app.use(express.urlencoded()); // Parse forms
// Custom middleware
app.use((req, res, next) => {
req.startTime = Date.now();
next();
});
// Error handling middleware (4 parameters)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
// Route-specific middleware
app.get('/secure', authenticate, (req, res) => {
res.json({ data: 'protected' });
});
function authenticate(req, res, next) {
if (req.headers.authorization) {
next();
} else {
res.status(401).json({ error: 'Unauthorized' });
}
}
// Session middleware
function sessionMiddleware(req, res, next) {
const sessionId = req.headers['x-session-id'];
req.session = sessions.get(sessionId) || {};
res.on('finish', () => {
sessions.set(sessionId, req.session);
});
next();
}
// Request logging
function 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();
}
// Request size limit
function limitRequestSize(maxSize) {
return (req, res, next) => {
let size = 0;
req.on('data', (chunk) => {
size += chunk.length;
if (size > maxSize) {
res.writeHead(413);
res.end('Payload Too Large');
req.destroy();
}
});
next();
};
}
// CORS middleware
function cors(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
} else {
next();
}
}
// Error wrapper for async routes
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next); // Pass errors to error middleware
};
}
// Error middleware
app.use((err, req, res, next) => {
console.error(err);
if (err instanceof ValidationError) {
res.status(400).json({ error: err.message, field: err.field });
} else if (err instanceof NotFoundError) {
res.status(404).json({ error: err.message });
} else {
res.status(500).json({ error: 'Internal Server Error' });
}
});
// Custom errors
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}