112

I am currently writing an API which will require a user to pass an authentication token in the header of each request. Now I know I can create a catchall route say

app.get('/*', function(req,res){

});

but I was wondering how do I make it so that it excludes certain routes such as /login or /?

clifford.duke
  • 3,980
  • 10
  • 38
  • 63
  • 2
    E.g Require authentication on all routes *except* **/login** and **/register**. (Since people are wondering why you asked this question) – a20 Sep 28 '15 at 08:47

5 Answers5

162

I'm not sure what you want to happen when a user accesses /login or /, but you can create separate routes for those; if you declare them before the catch-all, they get first dibs at handling the incoming requests:

app.get('/login', function(req, res) {
  ...
});

app.get('/', function(req, res) {
  ...
});

app.get('*', function(req, res) {
  ...
});
robertklep
  • 198,204
  • 35
  • 394
  • 381
  • 2
    This would only work for "GET"'s right? For some reason I can't get this answer to work with `app.use` (to include the other methods). – SSH This May 20 '16 at 21:57
  • 5
    @SSHThis I think that with the most recent version of Express (v4), `app.use('*', ...)` should work as well. But using [`app.all('*', ...)`](http://expressjs.com/en/4x/api.html#app.all) is preferable in this case. – robertklep May 21 '16 at 06:10
  • @JulienMartin just tested, looks like it works as expected with Express v4.16.3 – robertklep Sep 19 '18 at 17:38
  • `app.use` adds middleware, which by design mostly pass on the request to the next handler in the chain (using extra argument `next`). – Stijn de Witt Nov 06 '18 at 09:14
57

You can always place catch-all route after the ones you want to exclude (see robertklep answer).

But sometimes you simply don't want to care about the order of your routes. In this case you still can do what you want:

app.get('*', function(req, res, next) {
  if (req.url === '/' || req.url === '/login') return next();
  ...
});
Community
  • 1
  • 1
Leonid Beschastny
  • 50,364
  • 10
  • 118
  • 122
  • 1
    .get('*', function(req, res, next). – Oliver Dixon Aug 30 '15 at 10:52
  • 2
    @iLoveUnicorns thanx! Why didn't you just edit my post? – Leonid Beschastny Aug 30 '15 at 18:06
  • If the other rules are Before that one, is it needed to call next? – Sandburg Aug 01 '18 at 08:09
  • 1
    @Sandburg middleware should either call `next()` to pass execution to the next middleware, or send some response to finish processing the request. Otherwise request will hang. If the last middleware in the stack called `next()` then express will send the default "404 Not Found" response. Instead of calling `next()` you could, for example, call `res.status(400).end()` to finish request processing with a "400 Bad Request" response. – Leonid Beschastny Aug 01 '18 at 08:54
30

If you want to validate credentials or authenticity in every request you should use Express Routing feature "all", you can use it like this:

app.all('/api/*', function(req, res, next){
    console.log('General Validations');
    next();
});

You could place it before any Routing stuff.

Note that in this case I used "/api/" as path, you can use "/" you it fits your needs.

Hope it is not too late to help somebody here.

Seday Matzumiya
  • 421
  • 4
  • 2
12

Another way to make a catch-all route handler is this:

app.get('/login', function(req, res) {
  //... login page
});
app.get('/', function(req, res) {
  //...index page
});
app.get('/:pageCalled', function(req, res) {
  console.log('retrieving page: ' + req.params.pageCalled);
  //... mypage.html
});

This works exactly like robertklep's (accepted) answer, but it gives you more information about what the user actually requested. You now have a slug req.params.pageCalled to represent whatever page is being requested and can direct the user to the appropriate page if you have several different ones.

One gotchya to watch out for (thx @agmin) with this approach, /:pageCalled will only catch routes with a single /, so you will not get /route/1, etc. Use additional slugs like /:pageCalled/:subPageCalled for more pages (thx @softcode)

Jeremy Moritz
  • 13,864
  • 7
  • 39
  • 43
  • BETTER? Really? Don't you thing the circumstances dictate the definition? ;-) Unrelated: People downvoting without leaving a comment: How rude! – Potherca Mar 27 '15 at 23:26
  • 15
    there's a huge gotchya with this approach, `/:pageCalled` will only catch routes with a single `/`, so you will not get `/route/1` etc. – agmin Mar 30 '15 at 16:11
  • Also, if you do not surround it in a try/catch you will get an error if that page doesn't exist. – darethas Mar 30 '16 at 15:24
  • @agmin so would the solution not be to simply add `/:pageCalled/:subPageCalled` etc. ? – softcode Jan 17 '17 at 16:19
6

Negative lookahead regex

Maybe this will come handy at times:

const app = require('express')()
app.get(/^\/(?!login\/?$)/, (req, res) => { res.send('general') })
app.get('*',                (req, res) => { res.send('special') })
app.listen(3000)

With this, you would get:

/           general
/asdf       general
/login      special
/login/     special
/login/asdf general
/loginasdf  general

Or if you also want /login/asdf to be special:

app.get(/^\/(?!login($|\/.*))/, (req, res) => { res.send('general') })

In particular, when I Googled here I was thinking about the case of serving static frontend files on the same server that does the API for a SPA:

const apiPath = '/api';
const buildDir = path.join(__dirname, 'frontend', 'build');

// Serve static files like /index.html, /main.css and /main.js
app.use(express.static(buildDir));
// For every other path not under /api/*, serve /index.html
app.get(new RegExp('^(?!' + config.apiPath + '(/|$))'), function (req, res) {
  res.sendFile(path.join(buildDir, 'index.html'));
});

// Setup some actions that don't need to be done for the static files like auth.
// The negative lookahead above allows those to be skipped.
// ...

// And now attach all the /api/* paths which were skipped above.
app.get(apiPath, function (req, res) { res.send('base'); });
app.get(apiPath, function (req, res) { res.send('users'); });

// And now a 404 catch all.
app.use(function (req, res, next) {
  res.status(404).send('error: 404 Not Found ' + req.path)
})

// And now for any exception.
app.use(function(err, req, res, next) {
  console.error(err.stack)
  res.status(500).send('error: 500 Internal Server Error')
});

app.listen(3000)

Tested on express@4.17.1, Node.js v14.17.0.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    Thanks! I was surprised that on many threads nobody suggested regex. This is what worked for me `/^(?!\/login).+/` With your example `/^\/(?!login\/?$)/` for some reason it enters the route – LobsterMan Jun 22 '21 at 06:04
  • @LobsterMan it depends on what you want for `/login/asdf` I guess. Also you likely want instead `/^\/(?!login($|\/.*))/` otherwise you will catch `/loginasdf`. I added a bit more to the answer. – Ciro Santilli OurBigBook.com Jun 22 '21 at 10:08