Skip to content

Middleware ​

Middleware is just a function that sees req/res, maybe mutates them, then calls next() - or ends the response. Axios interceptors with a Node accent.

The shape ​

ts
function logger(req: express.Request, res: express.Response, next: express.NextFunction) {
  console.log(req.method, req.url)
  next() // hand off to the next layer
}

app.use(logger)

If you never call next() and never send a response, the request hangs. Classic "why is fetch pending forever" bug - except it's your server this time. 😅

Onion / pipeline ​

Registration order = execution order for incoming requests:

ts
app.use(cors({ origin: 'http://localhost:5173' }))
app.use(express.json())
app.use('/api/notes', requireAuth, notesRouter)

Built-ins you'll use constantly ​

ts
app.use(express.json()) // parse JSON bodies → req.body
app.use(express.urlencoded({ extended: true })) // form posts

Without express.json(), req.body is undefined and your POST handler looks "broken".

Route-level middleware ​

ts
function requireAuth(req, res, next) {
  const header = req.headers.authorization
  if (!header?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'unauthorized' })
  }
  // …verify JWT, attach req.user…
  next()
}

app.get('/api/me', requireAuth, (req, res) => {
  res.json({ user: req.user })
})

FE map:

FEExpress
Axios request interceptor adding Authorizationclient side
Server checks that headerrequireAuth
Vue Router beforeEach redirect to login401 + SPA handles it

We'll flesh out JWT verify in Protecting routes.

Error-handling middleware (preview) ​

Special signature with four args:

ts
app.use((err, req, res, next) => {
  console.error(err)
  res.status(500).json({ error: 'server error' })
})

Express only treats it as error middleware if it has arity 4. Details in the next-next chapter.

Don't do heavy work in every middleware ​

Logging is fine. Hitting the DB on every static asset request is not. Scope middleware to /api when it only matters there:

ts
app.use('/api', requireAuth) // careful: then *all* /api needs auth

Usually better: attach requireAuth per router or per sensitive route.

As you can see, middleware isn't a new paradigm - it's shared pipeline steps. Same idea you already use around fetch.

Best,

-- ł.

Notes-style Express tutorial for front-end folks