kreek
kreek

Reputation: 8834

Exclude route from express middleware

I have a node app sitting like a firewall/dispatcher in front of other micro services and it uses a middleware chain like below:

...
app.use app_lookup
app.use timestamp_validator
app.use request_body
app.use checksum_validator
app.use rateLimiter
app.use whitelist
app.use proxy
...

However for a particular GET route I want to skip all of them except rateLimiter and proxy. Is their a way to set a filter like a Rails before_filter using :except/:only?

Upvotes: 50

Views: 75745

Answers (11)

Executer
Executer

Reputation: 21

I wanted to make sure that if I hit all other methods except POST /user/login, then I should get 404 - Not Found And same for POST /user/signup

router.post("/signup", userSignup).all("*", (req, res) => res.sendStatus(404));
router.post("/login", userLogin).all("*", (req, res) => res.sendStatus(404));
router.get("/:id", getParticularUser);
router.patch("/:id", editUserDetails);

Above solved my problem, just posting if this is regarding what I wanted to know.

Upvotes: 0

jarora
jarora

Reputation: 5772

Improved upon @Geelie's answer with added types:

import {Request, Response, NextFunction, Handler} from "express";

const unless = (middleware: Handler, ...paths: RegExp[]): Handler => {
  return function (req: Request, res: Response, next: NextFunction) {
    const pathCheck = paths.some(path => path.test(req.path));
    pathCheck ? next() : middleware(req, res, next);
  };
};

app.use(unless(redirectPage, new RegExp("/user/login"), new RegExp("/user/register")));

Upvotes: 0

angy91m
angy91m

Reputation: 11

In my case I used part of answers posted yet to override original app.use

const unless = ( route, middleware ) => {
    return ( req, res, next ) => {
        if ( req.originalUrl.startsWith( route + '/' ) ) {
            return next();
        } else {
            return middleware( req, res, next );
        }
    };
};

const filteredRoute = '/myapi';  // Route to filter and subroute

const origUse = app.use;
app.use = function ( ...callbacks ) {
    if ( !callbacks.length ) throw new Error( '.use() method requires at least one function' );
    if ( typeof callbacks[0] ==='string' ) {
        if ( !( callbacks.length -1 ) ) throw new Error( '.use() method requires at least one function' );
        const route = callbacks.shift();
        for ( let i = 0; i < callbacks.length; i++ ) {
            origUse.call( this, route, unless( filteredRoute, callbacks[i] ) );
        }
    } else {
        for ( let i = 0; i < callbacks.length; i++ ) {
            origUse.call( this, unless( filteredRoute, callbacks[i] ) );
        }
    }
};

Upvotes: 1

lukaszfiszer
lukaszfiszer

Reputation: 2631

Even though there is no build-in middleware filter system in expressjs, you can achieve this in at least two ways.

First method is to mount all middlewares that you want to skip to a regular expression path than includes a negative lookup:

// Skip all middleware except rateLimiter and proxy when route is /example_route
app.use(/\/((?!example_route).)*/, app_lookup);
app.use(/\/((?!example_route).)*/, timestamp_validator);
app.use(/\/((?!example_route).)*/, request_body);
app.use(/\/((?!example_route).)*/, checksum_validator);
app.use(rateLimiter);
app.use(/\/((?!example_route).)*/, whitelist);
app.use(proxy);

Second method, probably more readable and cleaner one, is to wrap your middleware with a small helper function:

var unless = function(path, middleware) {
    return function(req, res, next) {
        if (path === req.path) {
            return next();
        } else {
            return middleware(req, res, next);
        }
    };
};

app.use(unless('/example_route', app_lookup));
app.use(unless('/example_route', timestamp_validator));
app.use(unless('/example_route', request_body));
app.use(unless('/example_route', checksum_validator));
app.use(rateLimiter);
app.use(unless('/example_route', whitelist));
app.use(proxy);

If you need more powerfull route matching than simple path === req.path you can use path-to-regexp module that is used internally by Express.

UPDATE :- In express 4.17 req.path returns only '/', so use req.baseUrl :

var unless = function(path, middleware) {
    return function(req, res, next) {
        if (path === req.baseUrl) {
            return next();
        } else {
            return middleware(req, res, next);
        }
    };
};

Upvotes: 116

Ashley Davis
Ashley Davis

Reputation: 10040

There's a lot of good answers here. I needed a slightly different answer though.

I wanted to be able to exclude middleware from all HTTP PUT requests. So I created a more general version of the unless function that allows a predicate to be passed in:

function unless(pred, middleware) {
    return (req, res, next) => {
        if (pred(req)) {
            next(); // Skip this middleware.
        }
        else {
            middleware(req, res, next); // Allow this middleware.
        }
    }
}

Example usage:

app.use(unless(req => req.method === "PUT", bodyParser.json()));

Upvotes: 5

Saikat Das
Saikat Das

Reputation: 368

The way I achieved this is by setting up a middleware for a specific path like so

app.use("/routeNeedingAllMiddleware", middleware1);
app.use("/routeNeedingAllMiddleware", middleware2);
app.use("/routeNeedingAllMiddleware", middleware3);
app.use("/routeNeedingAllMiddleware", middleware4);

and then setting up my routes like so

app.post("/routeNeedingAllMiddleware/route1", route1Handler);
app.post("/routeNeedingAllMiddleware/route2", route2Handler);

For the other special route that doesn't need all the middleware, we setup another route like so

app.use("/routeNeedingSomeMiddleware", middleware2);
app.use("/routeNeedingSomeMiddleware", middleware4);

and then setting up the corresponding route like so

app.post("/routeNeedingSomeMiddleware/specialRoute", specialRouteHandler);

The Express documentation for this is available here

Upvotes: 0

Richard Scarrott
Richard Scarrott

Reputation: 7063

Here's an example of using path-to-regexp as @lukaszfiszer's answer suggests:

import { RequestHandler } from 'express';
import pathToRegexp from 'path-to-regexp';

const unless = (
  paths: pathToRegexp.Path,
  middleware: RequestHandler
): RequestHandler => {
  const regex = pathToRegexp(paths);
  return (req, res, next) =>
    regex.exec(req.url) ? next() : middleware(req, res, next);
};

export default unless;

Upvotes: 0

Geelie
Geelie

Reputation: 571

Built upon the answer from @lukaszfiszer as I wanted more than one route excluded. You can add as many as you want here.

var unless = function(middleware, ...paths) {
  return function(req, res, next) {
    const pathCheck = paths.some(path => path === req.path);
    pathCheck ? next() : middleware(req, res, next);
  };
};

app.use(unless(redirectPage, "/user/login", "/user/register"));

Can't add as comment sorry.

Upvotes: 57

Guillaume Huard Hughes
Guillaume Huard Hughes

Reputation: 163

You can define some routes like below.

 app.use(/\/((?!route1|route2).)*/, (req, res, next) => {

    //A personal middleware
    //code

    next();//Will call the app.get(), app.post() or other
 });

Upvotes: 2

Guillaume
Guillaume

Reputation: 129

I use this regular expression with success : /^\/(?!path1|pathn).*$/.

Upvotes: 5

kj007
kj007

Reputation: 6254

You can also skip route like this by putting a condition on req.originalUrl:

app.use(function (req, res, next) {

    if (req.originalUrl === '/api/login') {
    return next();
    } else {
         //DO SOMETHING
    }

Upvotes: 24

Related Questions