Apply middleware to one of two routers

382 Views Asked by At

I have an express app with two routers, one of which I want to be protected with a middleware that verifies an app key, and another one that is "public"

However, when I try to add the middleware, it seems to apply to both routers or in other words, all my routes. I am trying to avoid adding the middleware to each specific route.

My implementation is as follows:

const main = express();

const protectedRouter = express.Router();
const unprotectedRouter = express.Router();

// This should NOT require validation, but it does
unprotectedRouter.all('/ping', (req, res) => res.send('Ping!'));

// This should require validation, and it does
protectedRouter.post('/academy/lesson-complete', ...onLessonCompleteSchemaChecks,  onLessonComplete);

// As you can see, I'm using both routers with base route /v2, I understand this shouldn't be a problem.
main.use('/v2', protectedRouter);
main.use('/v2', unprotectedRouter);

main.use(bodyParser.json());
main.use(bodyParser.urlencoded({ extended: false }));
main.use(errorHandler);

// THIS IS THE MIDDLEWARE, WHICH SHOULD ONLY APPLY TO ROUTES WITHIN PROTECTEDROUTER.
protectedRouter.use(validateAppToken);

//export to firebase functions
export const api = functions
  .runWith({
    memory: "512MB",
  })
  .region("europe-west2")
  .https
  .onRequest(main);
1

There are 1 best solutions below

0
jfriend00 On

The main issue here is that you have main.use('/v2', protectedRouter); defined first. So, ANY incoming request that starts with /v2 will be sent to protectedRouter. You then have protectedRouter.use(validateAppToken); so ALL routes that are sent to protectedRouter whether they match a route defined on that router or not, will execute the validateAppToken middleware. Thus, you get the problem you describe.

You have a several options:

  1. Put the protected routes and the unprotected routes on separate paths. Then, only the protected routes will match that app.use() and thus the protection middleware will only execute on routes that match the protected path.

  2. Register the protected router last. Then, the unprotected routes will match before the protection middleware gets to run. Routes are matched in the order they are registered and in the order routers are registered with the app. So, if the unprotected routes are registered first, then they will match before the protection middleware has a change to run.

  3. Instead of putting the middleware on the whole router which will execute for any /v2 route, put the protection middleware on each individual route definition so the protection middleware will only execute for routes that actually match a protected path definition.

As an example of option #2, you could just change this:

main.use('/v2', protectedRouter);
main.use('/v2', unprotectedRouter);

to this:

main.use('/v2', unprotectedRouter);
main.use('/v2', protectedRouter);

This will let unprotectedRouter match and run its routes BEFORE the protection middleware gets a chance to run as part of protectedRouter because protectedRouter will never even get called for unprotected route definitions.


What would be nice to have here (but I don't think Express has support for) is a middleware for protectedRouter that could be defined once, but would only be called if a route actually matches some route definition on protectedRouter. That is likely what you attempted to do, but Express doesn't quite work that way. When you define a generic middleware (with no path) on a router, that gets called for all requests that are routed to that router whenever the incoming request is passed to that router, even if nothing actually matches anything on that router.

Since your main.use('/v2', protectedRouter); came before the unprotected route definitions, that meant the protectedRouter.use(validateAppToken); was being executed, even for unprotected route definitions. That's just how Express works.