Express foundations
Follow one Express request
Trace a request through Node, Express middleware, a route handler, and the final response.
Express is a thin layer on top of the Node HTTP server. Node receives the bytes, parses them into a request, and hands the request to Express. Express then runs a list of functions in the order you registered them, until one of them sends a response.
That list is the whole framework. Once you can picture it, nothing in Express is mysterious anymore.
In this course we build a small notes app. Let’s follow a request for /notes through it.
flowchart LR
accTitle: One Express request
accDescr: Node receives an HTTP request, Express runs ordered middleware, a matching route handles it, and the response returns to the browser. Middleware can also forward an error.
Browser --> Node["Node HTTP server"]
Node --> Middleware["Ordered middleware"]
Middleware --> Route["Matching route"]
Route --> Response["HTTP response"]
Response --> Browser
Middleware -->|Error| Error["Error middleware"]
Route -->|Error| Error
Error --> Response
Middleware is a function in a line
A middleware is a function that receives the request, the response, and a next function. It can read the request, change it, send a response, or call next() to pass control to the next function in line.
A route handler is the same kind of function, registered for one method and path. It usually ends the request by sending a response.
Let’s see the order with three tiny middleware functions that log when they run:
import express from 'express'
const app = express()
app.use((req, res, next) => {
console.log('1 in')
next()
console.log('1 out')
})
app.use((req, res, next) => {
console.log('2 in')
next()
console.log('2 out')
})
app.get('/notes', (req, res) => {
console.log('handler')
res.send('3 notes')
})
app.listen(3000)
Run it and request http://localhost:3000/notes. The terminal prints:
1 in
2 in
handler
2 out
1 out
Notice the “out” lines. next() is a normal function call, so when the handler returns, control comes back up the chain. This is what lets a logger measure how long a request took: start a timer before next(), read it after.
Who ends the request
Exactly one function should end each request. It does that by sending a response with res.send(), res.json(), res.redirect() or similar.
If nobody sends a response and nobody calls next(), the request hangs. The browser spins until it times out. This is the most common beginner bug in Express, and you can spot it by asking: which function ends this request?
If a middleware calls next(err) with an error, or an async handler throws, Express skips the remaining normal functions and jumps to the error middleware, which we cover later in the course.
Express is not the server
Node still owns the socket, the parsing, the timeouts, and the listen() call. app.listen(3000) is a shortcut for http.createServer(app).listen(3000). Keep this in mind. When something looks like an Express problem but sits below the routing layer, like a connection that never closes, the answer is usually in Node.
Try this on your own: swap the order of the two app.use() calls, predict the new output, then run it and compare.
Lesson completed