Home > front end >  Express OPTIONS handler never gets a chance to run
Express OPTIONS handler never gets a chance to run

Time:09-30

I have some express routers specified like so:

const getFooRouter = Router().get('/foo', (_, res) => {
  res.json({ method: 'get', path: 'foo' })
})
const putFooRouter = Router().put('/foo', (_, res) => {
  res.json({ method: 'put', path: 'foo' })
})
const postBarRouter = Router().post('/bar', (_, res) => {
  res.json({ method: 'post', path: 'bar' })
})

Then I compose these into a single router representing the whole app logic

const mainRouter = Router().use(getFooRouter, putFooRouter, postBarRouter)

Finally I create an app, and run it like this:

const expressApp = express()
expressApp.use(mainRouter)
expressApp.listen(3333)

What I'm trying to do next, is add another Router that looks like this, to handle CORS, specific for each path:

const corsRouter = Router()
  .options('/foo', (_, res) => {
    res.json({ method: 'options', path: 'foo' })
  })
  .options('/bar', (_, res) => {
    res.json({ method: 'options', path: 'bar' })
  })

Then add this after my main router, so my app looks like this:

const expressApp = express()
expressApp.use(mainRouter)
expressApp.use(corsRouter)
expressApp.listen(3333)

The issue is, that none of the handlers get hit in my CORS router. Instead, when I hit for example OPTIONS http://localhost:3333/foo I'm getting something, what I assume express is doing on its own, which is a peculiar response with this body:

GET,HEAD

If I remove mainRouter, and my setup looks like this:

const expressApp = express()
expressApp.use(corsRouter)
expressApp.listen(3333)

The requests are going through fine to corsRouter. I'm using Insomnia for testing if it matter.

Question: What am I doing wrong in terms of structuring these routers?

NOTE:

  • Pease don't suggest ready-made CORS middlewares or similar stuff like that, I'm specifically interested why my corsRouter is not getting hit in this setup, and what do I need to do to correct this.
  • The reason it's structured like this, because this is generated from an OpenAPI document, and the structure lends itself nicely.

CodePudding user response:

It looks like a router instance, when hit with an OPTIONS request, will, if none of its routes matches the request but it has routes for other methods that match the same path, run a default OPTIONS handler which returns the response you see (GET,HEAD).

In your case the mainRouter is receiving requests first and it will run the aforementioned default handler.

If you switch around the order in which you mount the routers, it works as expected:

expressApp.use(corsRouter)
expressApp.use(mainRouter)

You can philosophise about whether this is a bug or not. My guess is that due to Express internals, it can't keep track if a (sub-)router has handled the OPTIONS response, so it will basically get default-handled by the first router that "finishes".

An alternative way of fixing this is to declare the OPTIONS handler on the router that handles the "regular" request.

  • Related