These ten questions cover the whole chapter, and they are the kind of thing an interviewer asks to find out whether you have really run an Express 24,430 server or only read about one. None of them is trivia: each turns on a behavior that catches people out in production. Work every answer out on paper first, then run the snippet and see how close you were. The answers, with the reasoning and the section that explains each one, are in Appendix G.
A scratch project is enough to check your work: npm 2,036 install express@5 in an empty directory, one file, and node --watch server.js. Where a question asks what the client sees, curl 3,008 -i shows you the status line and the headers as well as the body, which several of these questions depend on. Pin the version: three of the ten answers changed between Express 4 and Express 5, so an old copy of the framework in node_modules will quietly tell you the wrong thing.
Express 5
In each snippet app is a fresh express(), h is a handler that does nothing interesting, and no middleware is registered except what you can see. The version is Express 5.2.1 on Node.js 24 2,131 .
// 1. Two of these three registrations throw before the server ever starts.
// Which two, what is the error called, and why is the survivor legal?
app.get('/files/*', h);
app.get('/ab*cd', h);
app.get('/books/:id?', h);
// 2. What JSON comes back from GET /files/covers/2026/ab.png? What about GET / ?
// What would change if the second line read app.use('/{*splat}', ...) instead?
app.get('/files/*splat', (req, res) => res.json(req.params));
app.use('/*splat', (req, res) => res.status(404).json({ error: 'not found' }));
// 3. Request: GET /s?tag=js&tag=node&filter[year]=2026
// What is logged, and what is the response body? What would Express 4 have answered?
app.get('/s', (req, res) => {
try { req.query = { clean: true }; } catch (e) { console.log(e.constructor.name); }
res.json(req.query);
});
// 4. GET /boom answers with one of these two status codes. Which one, why is the other
// one never reached, and what did Express 4 do with this same route?
app.get('/boom', async () => { throw new Error('db down'); });
app.use((err, req, res) => res.status(500).json({ from: 'first' }));
app.use((err, req, res, next) => res.status(503).json({ from: 'second', seen: err.message }));The next four are about the order in which Express does things, and about the two APIs — req.params and the response builders — that carry the most hidden state.
// 5. GET /a and GET /b return different bodies, from handlers that differ by one call.
// What are the two bodies, and what does that tell you about when res.json() works?
app.get('/a', (req, res) => res.json({ ct: res.get('Content-Type') }));
app.get('/b', (req, res) => res.type('json').json({ ct: res.get('Content-Type') }));
// 6. Two pairs of handlers that look identical. One pair hands off, the other does not.
// What does GET /x return, and what does GET /y return?
app.route('/x').get((req, res, next) => next('route')).get((req, res) => res.send('second'));
app.get('/y', (req, res, next) => next('route'));
app.get('/y', (req, res) => res.send('second'));
app.use((req, res) => res.status(404).send('fell through'));
// 7. What does GET /a/7/c/42 return, and what does GET /b/7/c/42 return? And if the
// mount path were '/b/:id/c' instead, which :id would win inside the router?
const plain = express.Router(), merged = express.Router({ mergeParams: true });
for (const r of [plain, merged]) r.get('/:id', (req, res) => res.json(req.params));
app.use('/a/:pid/c', plain);
app.use('/b/:pid/c', merged);
// 8. Only these two routes are registered, and no cors middleware is in the stack. What
// status and which headers answer OPTIONS /books, PATCH /books and HEAD /books?
app.get('/books', (req, res) => res.send('list'));
app.post('/books', (req, res) => res.send('made'));If an answer surprises you, app.router.stack is the quickest way to see what Express actually built: it is a plain array of layers in registration order, each with a path or a route, and printing it settles Questions 6 and 8 without guesswork. For Question 4, adding a console.log to both error handlers tells you which one Express considers an error handler at all — the distinction is invisible to a type checker and to your editor.
NestJS 12
The last two use NestJS 12.0.4 79,377 on the Express adapter. log is a module-level array; a middleware bound to every route, a guard, an interceptor and a parameter-level pipe each push their own name into it as they run, and the interceptor pushes 'interceptor after' from a tap on the returned stream.
// 9. The guard's canActivate() returns false. What exactly is in `log` when the client
// receives its response, what status and body does the client get, and at which point
// would 'interceptor after' have been pushed if the guard had returned true?
@Get('order')
@UseGuards(Deny)
@UseInterceptors(Trace)
order(@Query('n', TracePipe) n: string) { log.push('handler'); return { log }; }
// 10. The application calls app.useGlobalPipes(new ValidationPipe()) with no options,
// so `transform` is off. What are the three values in the response to
// GET /types?limit=10, and which one is the trap?
@Get('types')
types(@Query('limit') limit: number) { return { limit, kind: typeof limit, sum: limit + 1 }; }