What Is Middleware in Express.js
SkillVeris Team
Engineering Team

Middleware in Express.js are functions that receive the request, response, and a next callback, and run in order between a request arriving and a response being sent.
In this guide, you'll learn:
- Each middleware can inspect or modify the request and response, then call next() to pass control to the following function in the chain.
- Express uses middleware for logging, body parsing, authentication, static files, and error handling.
- Order matters: middleware execute top to bottom in the sequence you register them.
- Error-handling middleware are special functions with four arguments, starting with the error object.
1What Is Middleware in Express.js?
Middleware in Express.js are functions that run in the middle of the request-response cycle — after a request arrives but before your final handler sends a response. Each one receives the request object, the response object, and a next function, and it can read or change the request, end the response, or hand off to the next middleware.
Think of a request as passing down an assembly line. Every station is a middleware: one logs the request, another parses the JSON body, another checks authentication. Control moves along the line each time a function calls next(), until some function finally responds.
2The Middleware Signature
Every middleware has the same shape: a function taking req, res, and next. Calling next() advances to the following middleware; calling res.send() or res.json() ends the cycle and stops the chain.
- function logger(req, res, next) {
- console.log(`${req.method} ${req.url}`)
- next() // pass control onward
- }
- app.use(logger) // register it globally
⚠️Always Resolve the Request
A middleware must either call next() or end the response. If it does neither, the request hangs forever and eventually times out — a common source of stuck endpoints.
3Types of Middleware
Express recognises several categories of middleware, distinguished by how and where they are registered.
- Application-level: registered with app.use() or app.get() and applied to the whole app or a path.
- Router-level: attached to an express.Router() instance to scope logic to a group of routes.
- Built-in: shipped with Express, such as express.json() and express.static().
- Third-party: installed from npm, like morgan for logging or cors for cross-origin headers.
- Error-handling: special four-argument functions that catch errors passed down the chain.
4Order Matters
Middleware run in the exact order you register them, top to bottom. This is the single most important thing to internalise: a body parser must be registered before the route that reads req.body, and an auth check must come before the handler it protects.
Registering middleware in the wrong order is a frequent cause of mysterious bugs — an undefined req.body usually means express.json() was placed after the route rather than before it.
- app.use(express.json()) // parse body first
- app.use(logger) // then log
- app.use('/api', authGuard) // then protect /api routes
- app.get('/api/users', handler) // finally handle
5Error-Handling Middleware
Error-handling middleware are special: they take four arguments — err, req, res, next — and Express routes errors to them automatically. When any middleware calls next(err) with an argument, Express skips the normal chain and jumps to the first error handler.
- function errorHandler(err, req, res, next) {
- console.error(err.stack)
- res.status(500).json({ error: 'Something broke' })
- }
- app.use(errorHandler) // register last, after all routes
Register It Last
Error handlers must be added after all other middleware and routes so that errors thrown anywhere upstream can flow down into them. Placing one too early means later routes have no handler to catch their failures.
6Writing Your Own Middleware
Custom middleware is just a function you control. A common pattern is a factory that returns a middleware, letting you configure behaviour per route without duplicating logic.
- function requireRole(role) {
- return (req, res, next) => {
- if (req.user?.role !== role) return res.status(403).end()
- next()
- }
- }
- app.get('/admin', requireRole('admin'), handler)
💡Attach Data to req
Middleware often stash results on the request, like req.user after verifying a token, so later handlers can use them without repeating the work.
7Common Mistakes to Avoid
Most middleware bugs come from a small set of oversights.
- Forgetting to call next() so the request stalls and never reaches the handler.
- Calling next() and then also sending a response, which triggers a headers-already-sent error.
- Registering a body parser after the route that needs req.body.
- Defining an error handler with three arguments instead of four, so Express treats it as normal middleware.
- Placing the error handler before routes, leaving downstream failures uncaught.
8Key Takeaways
Middleware is the core mental model for building anything in Express.
- Middleware are functions of req, res, and next that run between request and response.
- Each one calls next() to continue or ends the response to stop the chain.
- They handle logging, parsing, auth, static files, and errors.
- Registration order is execution order — arrange it deliberately.
- Error handlers take four arguments and are registered last.
9Frequently Asked Questions
Q: What happens if a middleware does not call next()? A: The request stops at that middleware. Unless it sends a response itself, the client waits until the connection times out. Every middleware must either call next() or end the response.
Q: How does Express know a function is an error handler? A: By its arity. If the function declares four parameters — err, req, res, next — Express registers it as an error-handling middleware and only invokes it when an error is passed down the chain.
Q: Can middleware run for only some routes? A: Yes. Pass a path as the first argument to app.use(), or attach the middleware directly to a specific route like app.get('/admin', guard, handler). Router-level middleware scope logic to a group of routes.
Q: What is the difference between app.use and app.get? A: app.use registers middleware that runs for all HTTP methods on a path, while app.get (or post, put, delete) registers a handler for one method. Both accept middleware functions in the same signature.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.