Use specific middleware in Express for all paths except a specific one

node.jsRoutesExpressMiddleware

node.js Problem Overview


I am using the Express framework in node.js with some middleware functions:

var app = express.createServer(options);
app.use(User.checkUser);

I can use the .use function with an additional parameter to use this middleware only on specific paths:

app.use('/userdata', User.checkUser);

Is it possible to use the path variable so that the middleware is used for all paths except a specific one, i.e. the root path?

I am thinking about something like this:

app.use('!/', User.checkUser);

So User.checkUser is always called except for the root path.

node.js Solutions


Solution 1 - node.js

I would add checkUser middleware to all my paths, except homepage.

app.get('/', routes.index);
app.get('/account', checkUser, routes.account);

or

app.all('*', checkUser);
    
function checkUser(req, res, next) {
  if ( req.path == '/') return next();

  //authenticate user
  next();
}

You could extend this to search for the req.path in an array of non-authenticated paths:

function checkUser(req, res, next) {
  const nonSecurePaths = ['/', '/about', '/contact'];
  if (nonSecurePaths.includes(req.path)) return next();

  //authenticate user
  next();
}

Solution 2 - node.js

You can set the middleware on each route also.

// create application/x-www-form-urlencoded parser
var urlencodedParser = bodyParser.urlencoded({ extended: false })

// POST /login gets urlencoded bodies
app.post('/login', urlencodedParser, function (req, res) {
  if (!req.body) return res.sendStatus(400)
  res.send('welcome, ' + req.body.username)
})

Solution 3 - node.js

Instead of directly registering User.checkUser as middleware, register a new helper function, say checkUserFilter, that gets called on every URL, but passed execution to userFiled` only on given URLs. Example:

var checkUserFilter = function(req, res, next) {
	if(req._parsedUrl.pathname === '/') {
		next();
	} else {
		User.checkUser(req, res, next);
	}
}

app.use(checkUserFilter);

In theory, you could provide regexp paths to app.use. For instance something like:

app.use(/^\/.+$/, checkUser);

Tried it on express 3.0.0rc5, but it doesn't work.

Maybe we could open a new ticket and suggest this as a feature?

Solution 4 - node.js

Use

app.use(/^(\/.+|(?!\/).*)$/, function(req, resp, next){...

This pass any url apart from /. Unless, it works for me.

In general

/^(\/path.+|(?!\/path).*)$/

(see https://stackoverflow.com/questions/1240275/how-to-negate-specific-word-in-regex)

Hope this helps

Solution 5 - node.js

Use this library called express-unless

Require authentication for every request unless the path is index.html.

app.use(requiresAuth.unless({
  path: [
    '/index.html',
    { url: '/', methods: ['GET', 'PUT']  }
  ]
}))

Path it could be a string, a regexp or an array of any of those. It also could be an array of object which is URL and methods key-pairs. If the request path or path and method match, the middleware will not run.

This library will surely help you.

Solution 6 - node.js

The solution is to use order of setting api and middleware. In your case it must be something like this.

 var app = express.createServer(options);
    
    // put every api that you want to not use checkUser here and before setting User.checkUser
    app.use("/", (req, res) => res.send("checkUser middleware is not called"));
    
    
    app.use(User.checkUser);
    
    // put every api that you want use checkUser
    app.use("/userdata", User.checkUser, (req, res) =>
      res.send("checkUser called!")
    );

This is a full example.

const express = require("express");
const app = express();
const port = 3002;

app.get("/", (req, res) => res.send("hi"));

app.use((req, res, next) => {
  console.log("check user");
  next();
});

app.get("/checkedAPI", (req, res) => res.send("checkUser called"));

app.listen(port, () => {
  console.log(`Server started at port ${port}`);
});

Solution 7 - node.js

The solution by @chovy is the best.

Another solution is:

I was facing a similar problem. So what I did was to split the functions in the "routes" file into different files and export them separately. Likewise I imported them in the "server" file separately and called the middlewares for the respective routes. But I wouldn't recommend this on a large scale, I had a small project of less than 10 routes so I didn't mind doing it. For scaling I would go for chovy's method.

Solution 8 - node.js

Thing that worked for me is to init single route you want before initializing for-all check, for example:

var app = express.createServer(options);

app.get('/', routes.index);

app.use(User.checkUser);

My personal example is this and works:

const app = express();

...

app.use('/api/flow', flowLimiter, flowRouter);

app.use(csurf({
	cookie: false,
}));

...

So csurf is applied an all routes except api/flow - where I use sendBeacon, so I couldn't apply headers with tokens to it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThomasView Question on Stackoverflow
Solution 1 - node.jschovyView Answer on Stackoverflow
Solution 2 - node.jsJonatan Lundqvist MedénView Answer on Stackoverflow
Solution 3 - node.jsjsalonenView Answer on Stackoverflow
Solution 4 - node.jsuser1872904View Answer on Stackoverflow
Solution 5 - node.jsAbhay ShiroView Answer on Stackoverflow
Solution 6 - node.jsMilad RaeisiView Answer on Stackoverflow
Solution 7 - node.jstheBirdView Answer on Stackoverflow
Solution 8 - node.jsRomanistHereView Answer on Stackoverflow