Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Express OPTIONS handler never gets a chance to run

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading