Why does ExpressJS not match all paths on root app.get('/' - express

The following express routing matches GET / but not GET /anything/else.
app.get('/', (req, res, next) => {
res.send('I only answer to /');
});
Is Express Routing not prefix-based or "deep"?
I know this works but it seems wrong:
app.get('*', (req, res, next) => {
res.send('I answer to any path');
});

app.get must match the full path and stores it in req.path, whereas app.use matches a prefix and sets req.path to the path after the prefix. You could write
app.use('/', (req, res, next) => {
if (req.method === "GET")
res.send('I answer to all GET requests');
else
next();
});
GET /anything/else would be matched by app.use('/anything', ...), but then req.path = '/else'.

One possible fix although it's not as elegant would be:
app.use(function(req, res, next) => {
res.send('I answer to any path')
});
Make sure to add this to the end of all your Routing. Hope this still helps :)
Have a nice day

Related

express order difference make result different

const express = require('express')
const app = express();
app.use('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
app.use('/', (req, res, next) =>{
res.send('Hello from express.')
});
app.listen(4000, function () {
console.log('console server listening on 4000');
})
when i go to to http://localhost:4000/users it will render: Hello from express Again..
BUT if I change the code order like:
app.use('/', (req, res, next) =>{
res.send('Hello from express.')
});
app.use('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
Now I do it again: http://localhost:4000/users The result is Hello from express.
I guess it's configuration issue. But I just don't know which configuration.
Yes, the order you declare your routes in Express is important. Routes are matched from first to last in order of declaration and the first route that matches gets the request. If it sends a response and doesn't call next(), then the following routes do not get a chance to run.
Also relevant here is that app.use(somePath, ...) will match any route that starts with somePath. So, this:
app.use('/', (req, res, next) =>{
res.send('Hello from express.')
});
will match every single possible route because they all start with "/".
So, when you do this:
app.use('/', (req, res, next) =>{
res.send('Hello from express.')
});
app.use('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
And, send a request for /users, then the first route will match everything and thus the second route is never called.
Whereas, when do this:
app.use('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
app.use('/', (req, res, next) =>{
res.send('Hello from express.')
});
The /users route gets the first chance to match so when you send a request for /users, that /users route gets matched first and takes the request.
Note: You should only be using app.use() when you either want to match all possible http verbs or you WANT the greedy partial match that is in play here. If you don't want one of those, then use the verb-specific version such as app.get(). In your example, if you make both of these app.get(), then it is no longer order-sensitive because app.get() only matches complete path matches and does not do partial matches.
Both of these work the same:
app.get('/', (req, res, next) =>{
res.send('Hello from express.')
});
app.get('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
and:
app.get('/users', (req, res, next) =>{
res.send('Hello from express Again.')
});
app.get('/', (req, res, next) =>{
res.send('Hello from express.')
});
Because only one of them will match / and only one will match /users.

How to pass arguments to a callback function in express?

I'm using express middleware with NodeJS. I organized my routes with no callbacks, but for some of them it's better to mutualize functions, so I began to write some callbacks.
I have for example this function (I don't put the whole code, it's useless for my question here):
async function verification(req, res, next) {
And I use it like this in the route part:
router.post('/item/:id/dosomething',
verification,
async (req, res) => {
Everything works well, but if I want to continue using callbacks (sometimes it's a good solution to have a clean and efficient code), I'll have to pass some arguments to the function. Of course I tried this, and it didn't work:
router.post('/item/:id/dosomething',
verification(arg1, arg2),
async (req, res) => {
I searched on StackOverflow answers for "pass arguments to a callback function" but the few that could be interesting talked about a wrapping function, which I don't think I can implement in my case.
If anybody could give me a hand, that would be great. Thanks :)
Here is a snippet of how a next worked without writing it in the call (look at the verif):
The callback:
async function verif(req, res, next) {
let rows;
const {
id
} = req.params;
({ rows } = await db.query(`XXX`))
if (rows.length === 1) {
return next();
} else {
retour(req, res, 500, "No can do.");
}
Where it's called:
router.post('/mymusic/:id/addElements',
droits.verifRightsCB.bind(undefined, 'music', 'addInMyMusic'),
verif,
async (req, res) => {...
The last part of the route (async (req, res) =>) is only executed when the next() condition is fulfilled in the verif, although I didn't pass any argument.
function verification(arg1, arg2, req, res, next) {...}
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, arg2),
(req, res) => {...}
(The functions need not be async.)
Note that the router.post statement is executed during server start-up, not per request. Therefore you cannot write something like
router.post('/item/:id/dosomething',
verification.bind(undefined, arg1, req.params.arg),
(req, res) => {...}
because there is no req during server start-up. Instead, you may write
router.post('/item/:id/dosomething',
(req, res, next) => verification(arg1, req.params.arg, req, res, next),
(req, res) => {...}
The verification function can then
call next() after successful verification to invoke the other middleware function ((req, res) => {...})
or call next(err) after a verification error in order to skip the other middleware function and report the error.

How can I route all calls starting with string api to their handlers in an express middleware

I have a express app.js with typical
app.get('/path1', (req, res => {})
app.get('/path2', (req, res => {})
app.get('/path3', (req, res => {})
now I want to catch all routes, starting with api such as below and redirect them to their corresponding handler in express but not sure how to achieve that
/api/path1
/api/path2
/api/path3
I' assuming i can have a catch all api as below
app.all('/api/*', function (request, response, next) { //in a server.js file
//how can i call the corresponding paths here??
// looking for something to do forward to current-route.replace('api','')
// or something like that
})
Maybe a router-level middleware could solve your problem:
const router = express.Router();
router.get('/path1', (req, res => {});
router.get('/path2', (req, res => {});
router.get('/path3', (req, res => {});
app.use('/api', router);
Update:
Use redirect (not that much of a difference to your current solution; not tested):
app.all('/api/*', (request, response) => res.redirect(request.url.replace('/api', '')));
this worked for me, please let me know if there is a better way
app.all('/api/*', function (request, response, next) {
request.url = request.url.replace('/api','');
next();
})

How to isolate or fix no-response (browser hang) after calling passport.authenticate as middleware

I'm trying to use passport.authenticate('local') as middleware with passport-local-mongoose but it doesn't seem to pass control to the subsequent middlewares in my route.
I'm assuming there's an error but the following (err, req, res, next) middleware isn't being called so I'm wondering if I've misunderstood something basic about passport-local in general.
I've spent a few hours trying various things, searching here but I can't find a way to isolate my problem any further or get a better log of where control is going wrong in my route.
I've posted a small reproducible example to GitHub.
This is how I'm setting up BEFORE my routes:
// Get connected
mongoose.connect('mongodb://localhost/pass');
mongoose.Promise = global.Promise;
mongoose.connection.on('error', (err) => { console.error(err.message) });
// Basic user schema using nickname field as username
const Schema = mongoose.Schema;
const userSchema = new Schema({});
userSchema.plugin(passportLocalMongoose, { usernameField: 'nickname' });
const User = mongoose.model('User', userSchema);
// Initialise passport before routes
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser);
passport.deserializeUser(User.deserializeUser);
app.use(passport.initialize());
And this is the route with my passport.authenticate:
app.post('/login',
(req, res, next) => {
console.log('login posted ok');
next();
},
passport.authenticate('local'),
(req, res) => res.send('login successful'),
(err, req, res, next) => {
console.log(err);
res.send('login unsuccessful');
}
);
There are other routes with the pug views and registration.
Registration works fine, in mongo db.users.find() show a good looking entry for the new user.
But my /login post route doesn't get beyond passport.authenticate.
The console.log gets triggered, so I know the route is being called.
Based on my limited knowledge of express and passport, I'm expecting one of those two following middlewares to be triggered, one on success and one if it fails.
Neither is triggering.
Best way to isolate is covered in the authenticate docs under "Custom Callback", I just didn't understand it originally.
NOTE: I've saved the following in the answer branch on my repo, as posted in the question.
app.post('/login',
(req, res, next) => {
console.log('login posted ok');
next();
},
(req, res, next) => {
console.log('authenticating...')
passport.authenticate('local', (err, user, info) => {
console.log(req, res, err, user, info);
next(err);
}) (req, res, next);
},
(req, res) => res.send('login successful'),
(err, req, res, next) => {
console.log(err);
res.send('login unsuccessful');
}
);
What I realised
passport doesn't consider authentication FAIL as an err
it just leaves user null
basically you need to give it success and failure redirects if you want to use middleware form, don't do what I did and try to handle err etc.

Express: Render partially in each handler?

I have a modest little site that uses Express and Pug. Every page has a navbar that contains the user's name drawn from a user parameter and the main content of the page is below which uses a data parameter. The main content is rendered from a template that extends the root template. Both need their own parameter to render properly.
Is there a way to partially render a view in each handler?
var app = require('express')()
app.set('view engine', 'pug')
app.get('/', (req, res, next) => {
var userdata = '...'
req.render('/index', {user: userdata}
next() // the request is for /page
})
app.get('/page', (req, res, next) => {
var moredata = '...'
req.render('./page/index', {data: mroedata})
})
AFAIK that's not possible, but as an alternative, you can use res.locals to achieve something similar:
let userMiddleware = (req, res, next) => {
let userdata = '...';
res.locals.user = userdata;
next();
});
app.use(userMiddleware);
// This can stay the same:
app.get('/page', (req, res, next) => {
var moredata = '...'
req.render('./page/index', {data: mroedata})
})
This is a middleware that will make the user variable available to all of your templates, which seems to me is what you want.
Instead of applying it globally, you can also use it for specific routes:
app.get('/page', userMiddleware, (req, res) => ...);