Suggest an editImprove this articleRefine the answer for “What are the different types of middleware in Express.js?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Middleware in Express.js** - functions that run between receiving a request and sending a response, each with access to `req`, `res`, and `next()`. ```js app.use((req, res, next) => { console.log(req.method); // runs on every request next(); }); ``` Express has 5 types: application-level (`app.use()`), router-level (`router.use()`), error-handling (4-param signature `(err, req, res, next)`), built-in (`express.json()`), and third-party (`helmet`, `cors`). **Key rule:** mount error handlers last, always call `next()` in async middleware.Shown above the full answer for quick recall.Answer (EN)Image**Middleware in Express.js** - functions that run between receiving an HTTP request and sending a response, each with access to `req`, `res`, and `next()`. ## Theory ### TL;DR - Express has 5 middleware types: application-level, router-level, error-handling, built-in, third-party - Think of it as an assembly line: each station processes the request, then passes it forward with `next()` - Application-level runs globally; router-level is scoped to a path prefix - Error-handling middleware uses a 4-parameter signature `(err, req, res, next)` and must be mounted last - No `next()` call in a handler means the request hangs forever ### Quick example ```js const express = require('express'); const app = express(); // Application-level: runs on all requests app.use((req, res, next) => { console.log(`${req.method} ${req.path}`); // GET /api/users next(); }); // Router-level: scoped to /api const router = express.Router(); router.use((req, res, next) => { console.log('Router hit'); // only fires for /api/* requests next(); }); app.use('/api', router); ``` On `GET /api/users`, both logs fire in order. On `GET /`, only the first one fires. That one difference explains the whole app vs router split. ### Middleware types **1. Application-level** Bound to the `app` instance via `app.use()` or `app.METHOD()`. Runs globally or for a specific path. ```js app.use(express.json()); // all routes app.get('/users', (req, res, next) => { next(); }); // GET /users only ``` **2. Router-level** Bound to `express.Router()`. Identical behavior to application-level, but scoped to the mount path. This is the standard way to split a large API into modules. ```js const userRouter = express.Router(); userRouter.use(authCheck); // runs only for /api/users/* app.use('/api/users', userRouter); ``` **3. Error-handling** Express identifies this type by the 4-parameter signature `(err, req, res, next)`. When any middleware calls `next(err)`, Express skips to this handler. Always mount it last. ```js app.use((err, req, res, next) => { res.status(err.status || 500).json({ error: err.message }); }); ``` **4. Built-in** Shipped with Express since version 4.16. Three functions cover the most common parsing needs: ```js app.use(express.json()); // parses JSON bodies app.use(express.urlencoded({ extended: true })); // parses form data app.use(express.static('public')); // serves static files ``` **5. Third-party** npm packages that plug into the same middleware system: ```js app.use(require('helmet')()); // security headers app.use(require('cors')()); // CORS headers app.use(require('morgan')('dev')); // request logging ``` ### Execution order Express keeps a stack of middleware functions per app or router, executed in mount order. When a request arrives, Express walks the stack top to bottom. Each function calls `next()` to continue, sends a response to stop, or calls `next(err)` to jump to the error handler. ```js app.use(cors()); // 1 app.use(helmet()); // 2 app.use(express.json()); // 3 app.use('/api', routes); // 4 - route handlers app.use(notFound); // 5 - 404 catch-all app.use(errorHandler); // 6 - must be last ``` Router stacks are sub-stacks merged into the app stack at the mount path. So `userRouter.use(authCheck)` combined with `app.use('/api', userRouter)` means `authCheck` only runs when the path starts with `/api`. ### Common mistakes **Forgetting `next()` in async middleware** ```js // Wrong - request hangs forever app.use(async (req, res, next) => { await db.connect(); // no next() here }); // Right app.use(async (req, res, next) => { try { await db.connect(); next(); } catch (err) { next(err); } }); ``` **Mounting the error handler too early** ```js // Wrong - error handler defined before routes app.use((err, req, res, next) => { res.status(500).send('Error'); }); app.get('/', (req, res) => res.send('Hi')); // Right app.get('/', (req, res) => res.send('Hi')); app.use((err, req, res, next) => { res.status(500).send('Error'); }); ``` **Registering `express.json()` after the router** The body-parser ordering issue catches almost everyone at some point. It's the first thing to check when `req.body` shows up as undefined. ```js // Wrong - req.body is undefined inside userRouter app.use('/api', userRouter); app.use(express.json()); // too late // Right app.use(express.json()); // body parser first app.use('/api', userRouter); ``` **Uncaught async errors in Express 4** An `async` function that throws does not automatically call `next(err)` in Express 4. The error bypasses the error handler entirely. ```js // Wrong in Express 4 - error never reaches the error handler app.get('/data', async (req, res, next) => { throw new Error('Boom'); }); // Right - explicit try/catch app.get('/data', async (req, res, next) => { try { const data = await fetchData(); res.json(data); } catch (err) { next(err); } }); ``` Express 5 (currently in beta) handles async throws natively. **Overlapping router paths shadow each other** ```js app.use('/users', userRouter); // matches, runs app.use('/users/:id', idRouter); // never runs - shadowed by line above ``` Fix: register routers from specific to general, or consolidate the logic into one router. ### Real-world usage - `morgan` for HTTP logging in almost every Express app: `app.use(morgan('combined'))` - `helmet` as a default security baseline: `app.use(helmet())` - Router-level auth for `/api/v1` route groups in REST APIs - NestJS uses application-level middleware for guards and interceptors under the hood - NextAuth.js wraps its `/api/auth` handler group in `express.Router()` ### Follow-up questions **Q:** What is the execution order with nested routers? **A:** App-level middleware runs first, then the outer router's middleware, then the inner router's, then the route handler. `next()` chains them linearly top to bottom. **Q:** How does `app.METHOD()` differ from `app.use()`? **A:** `app.get()` matches only GET requests and an exact path. `app.use()` matches all HTTP methods and any path that starts with the given prefix. That is why global middleware always uses `app.use()`. **Q:** What happens if middleware calls `next()` twice? **A:** The second call is ignored. No error is thrown, but behavior becomes unpredictable if a response was already sent by then. **Q:** What is the difference between `express.json()` and `body-parser`? **A:** No practical difference. `body-parser` was folded into Express core in version 4.16. `express.json()` calls the same underlying code. **Q (senior):** In a microservice with 50+ middleware functions, how do you keep the stack manageable without losing execution order guarantees? **A:** Group related functions into composed arrays: `const apiPipeline = [cors(), helmet(), express.json()]`, then `app.use('/api', apiPipeline, routes)`. Profile bottlenecks with `clinic.js` or `0x`. Avoid synchronous blocking work anywhere in the stack. ## Examples ### Basic: global logging and JSON parsing ```js const express = require('express'); const app = express(); // Built-in: parse JSON bodies before any route reads req.body app.use(express.json()); // Application-level: log every incoming request app.use((req, res, next) => { console.log(`${req.method} ${req.path}`); // GET /users next(); }); app.get('/users', (req, res) => { res.json([{ id: 1, name: 'Alice' }]); }); app.listen(3000); ``` Every request hits the logger first, then the route handler. Swap the order of those two `app.use()` calls and the logger still works, but if you add a POST route that reads `req.body`, the body will be undefined. ### Intermediate: router-level auth guard ```js const express = require('express'); const app = express(); const userRouter = express.Router(); app.use(express.json()); // Router-level: token check only for /api/users userRouter.use((req, res, next) => { if (req.headers.authorization !== 'Bearer secret') { return res.status(401).json({ error: 'Unauthorized' }); } next(); }); userRouter.post('/', (req, res) => { res.status(201).json({ id: 1, name: req.body.name }); // { id: 1, name: 'Alice' } }); app.use('/api/users', userRouter); app.listen(3000); // POST /api/users without token -> 401 // POST /api/users with Bearer secret -> 201 ``` The auth check runs only on `/api/users/*`. A public route like `GET /health` never touches it. That is the concrete benefit of keeping middleware at the router level instead of global. ### Senior: async error handling across the full pipeline ```js const express = require('express'); const app = express(); app.use(express.json()); const findUser = (id) => { if (id !== '1') { const err = new Error('User not found'); err.status = 404; throw err; } return { id: 1, name: 'Alice' }; }; app.get('/users/:id', async (req, res, next) => { try { const user = findUser(req.params.id); res.json(user); } catch (err) { next(err); // routes to error handler below } }); // 4 parameters, mounted last app.use((err, req, res, next) => { console.error(err.stack); // Error: User not found\n at ... res.status(err.status || 500).json({ error: err.message }); }); app.listen(3000); // GET /users/1 -> { id: 1, name: 'Alice' } // GET /users/99 -> 404 { error: 'User not found' } ``` Drop the `try/catch` and throw directly in the `async` handler in Express 4, and the error never reaches the error middleware. The process crashes in production instead. Always wrap async route logic explicitly until Express 5 becomes stable.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.