What is Middleware in Express and How It Works

Search for a command to run...

No comments yet. Be the first to comment.
You open WhatsApp, type a message, and hit send. A single grey tick appears. You're on a train with no signal, but the message went through anyway. Or did it? That single tick is the beginning of a su

You record a Reel, add a song, trim it, and then close the app before posting. When you reopen Instagram, the draft is right there waiting. Nothing was lost. That experience feels simple. The system b

If you have built a React Native app, you have felt the pain of navigation setup. It is easy to get wrong and hard to keep clean as apps grow. This article explains what routing means, why it matters,

Modern apps are not just a pile of screens. They are a living system of features, data flows, offline behavior, and performance constraints. If you have only shipped small apps, the jump to something

Your application has user accounts. Some routes should only be accessible to logged-in users. How do you protect routes and verify that a request actually comes from who they claim to be? JWT (JSON We

Ashish's Blog
57 posts
Middleware sits between incoming requests and your route handlers. It intercepts requests, does something with them, and either passes them along or stops them. Understanding middleware is key to building Express applications.
Middleware is a function that has access to the request and response objects. It can modify them, perform checks, or stop the request.
Think of middleware as checkpoints in an airport:
Security check (middleware validates the request)
Baggage scan (middleware processes data)
Gate (final destination/route handler)
Each checkpoint can allow you through to the next, or stop you.
In Express, middleware functions have this signature:
function middleware(req, res, next) {
// Do something
next(); // Pass to next middleware
}
The next() function passes control to the next middleware or route handler.
When a request arrives, Express processes it through middleware in order:
Request comes in
|
v
Middleware 1 (if next() called, continue)
|
v
Middleware 2 (if next() called, continue)
|
v
Route Handler (your actual route)
|
v
Response sent
Each middleware either calls next() to continue or sends a response to stop.
Express provides built-in middleware for common tasks:
import express from 'express';
const app = express();
// Parse JSON requests
app.use(express.json());
// Parse URL-encoded form data
app.use(express.urlencoded({ extended: true }));
// Serve static files
app.use(express.static('public'));
app.post('/data', (req, res) => {
// Thanks to express.json() middleware, req.body is parsed
console.log(req.body);
res.send('Data received');
});
app.listen(3000);
express.json() middleware automatically parses incoming JSON and populates req.body.
Write your own middleware for custom logic:
import express from 'express';
const app = express();
// Custom logging middleware
function logger(req, res, next) {
console.log(`\({req.method} \){req.url}`);
next(); // Pass to next middleware
}
app.use(logger);
app.get('/', (req, res) => {
res.send('Home page');
});
app.listen(3000);
Every request is logged, then passed to the route handler.
Output for GET /:
GET /
Middleware runs in the order you define it:
import express from 'express';
const app = express();
app.use((req, res, next) => {
console.log('1: First middleware');
next();
});
app.use((req, res, next) => {
console.log('2: Second middleware');
next();
});
app.get('/', (req, res) => {
console.log('3: Route handler');
res.send('Done');
});
app.listen(3000);
For a GET / request, output is:
1: First middleware
2: Second middleware
3: Route handler
Order matters. Middleware defined first runs first.
next() FunctionCalling next() passes control to the next function in the chain. Not calling it stops execution:
app.use((req, res, next) => {
console.log('Middleware 1');
next(); // Continue
});
app.use((req, res, next) => {
console.log('Middleware 2');
// Not calling next() - stops here
});
app.get('/', (req, res) => {
console.log('Route'); // Never reached
res.send('Hi');
});
This stops at "Middleware 2". The route handler is never called.
Runs for all routes or specific routes:
import express from 'express';
const app = express();
// Runs for ALL routes
app.use((req, res, next) => {
console.log('Global middleware');
next();
});
// Runs only for /admin routes
app.use('/admin', (req, res, next) => {
console.log('Admin middleware');
next();
});
app.get('/', (req, res) => res.send('Home'));
app.get('/admin/users', (req, res) => res.send('Admin page'));
app.listen(3000);
Middleware on a specific router:
import express from 'express';
const app = express();
const router = express.Router();
router.use((req, res, next) => {
console.log('Router middleware');
next();
});
router.get('/users', (req, res) => res.send('Users'));
app.use('/api', router);
app.listen(3000);
Express provides standard middleware:
app.use(express.json());
app.use(express.static('public'));
app.use(express.urlencoded({ extended: true }));
Use packages from npm:
npm install cors
import cors from 'cors';
app.use(cors()); // Enable CORS
function authenticateToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).send('No token');
}
// Verify token (simplified)
if (token === 'valid-token') {
req.user = { id: 1 };
next();
} else {
res.status(403).send('Invalid token');
}
}
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Hello user ${req.user.id}`);
});
app.get('/data', (req, res, next) => {
try {
throw new Error('Something went wrong');
} catch (error) {
next(error); // Pass to error handler
}
});
// Error handling middleware (must have 4 parameters)
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).send('Server error');
});
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
console.log(`\({req.method} \){req.url} - ${duration}ms`);
});
next();
});
Middleware factories return middleware functions:
function logMessages(format) {
return (req, res, next) => {
if (format === 'simple') {
console.log(`\({req.method} \){req.url}`);
} else if (format === 'detailed') {
console.log(`\({req.method} \){req.url} - ${req.headers['user-agent']}`);
}
next();
};
}
app.use(logMessages('simple'));
// or
app.use(logMessages('detailed'));
import express from 'express';
const app = express();
// Built-in middleware
app.use(express.json());
// Custom logging middleware
app.use((req, res, next) => {
console.log(`[\({new Date().toISOString()}] \){req.method} ${req.url}`);
next();
});
// Authentication middleware
function authenticate(req, res, next) {
const token = req.query.token;
if (token === 'secret') {
req.user = { id: 1, name: 'John' };
next();
} else {
res.status(401).send('Unauthorized');
}
}
// Request validation middleware
function validateRequest(req, res, next) {
if (!req.body.email) {
return res.status(400).send('Email required');
}
next();
}
// Public route
app.get('/home', (req, res) => {
res.send('Welcome');
});
// Protected route
app.get('/profile', authenticate, (req, res) => {
res.send(`Hello ${req.user.name}`);
});
// Route with validation
app.post('/register', validateRequest, (req, res) => {
res.send(`Registered ${req.body.email}`);
});
// Error handling middleware (must be last)
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ error: 'Server error' });
});
app.listen(3000, () => {
console.log('Server running');
});
Mistake 1: Forgetting to call next()
// Wrong - middleware stops here
app.use((req, res, next) => {
console.log('Middleware');
// No next() call - route never executes
});
app.get('/', (req, res) => {
res.send('Hello'); // Never reached
});
// Right
app.use((req, res, next) => {
console.log('Middleware');
next(); // Passes to next handler
});
Mistake 2: Wrong middleware order
// Wrong - middleware after route can't help
app.get('/', (req, res) => {
res.send(req.body); // undefined - JSON not parsed yet
});
app.use(express.json()); // Too late!
// Right - middleware before routes
app.use(express.json());
app.get('/', (req, res) => {
res.send(req.body); // Properly parsed
});
Mistake 3: Error handler must have 4 parameters
// Wrong - Express won't recognize as error handler
app.use((err, req, res) => {
res.send('Error');
});
// Right - all 4 parameters required
app.use((err, req, res, next) => {
res.send('Error');
});
Middleware intercepts requests and can modify them or stop them
Middleware runs in the order it's defined
Call next() to pass to the next middleware
Express provides built-in middleware like express.json()
You can create custom middleware for any task
Application-level middleware runs for all/specific routes
Error handlers are middleware with 4 parameters
Order matters: define middleware before routes that need it
Middleware is the backbone of Express applications. Master it, and you can build clean, modular, and powerful web applications.