What is the specific function of next() in NodeJS/SailsJS? - javascript

I've been watching a SailsJS tutorial and for some user authentication I saw that function applied but I did not get what is the specific function of next();

The purpose of next is to continue processing a request -- this allows things like middleware to work.
Here's an example.
Imagine you have a simple Express.js app that looks like this:
var app = express();
app.get('/', function(req, res) {
res.send('hi!');
});
app.get('/dashboard', function(req, res) {
res.send('there!');
});
The above app has 2 routes, and each does something particular.
Now, what if we wanted to create a special function that prints hello world to the console before every request is executed? To do that, we'd need to modify either both routes above, or create a middleware like so:
var app = express();
app.use(function(req, res, next) {
console.log('hello, world!');
next(); // continue processing
});
app.get('/', function(req, res) {
res.send('hi!');
});
app.get('/dashboard', function(req, res) {
res.send('there!');
});
Now, what happens is that, for each request, the function we defined above will be called BEFORE any of our route code, and once it makes the call to next(), that means we'll then run our route code.
Nifty, right?
At a basic level, the next object is just the 'next function' to execute, so when you say next(), you're telling express to keep processing the user request, essentially.

Related

NodeJS middleware calling order

Why res.send("main page 2") not overriding res.send("main page 1") when I request localhost:3000 ?
While execucing this code in NodeJS only console.log("midddleware") is calling from app.use method but not res.send. I wonder why it works like that.
const express = require('express')
const app = express()
app.get('/', function(req, res){
res.send("main page 1")
})
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware")
})
app.listen(3000)
You are likely being tricked by the second request (for the favicon.ico) that the browser sends to your server.
Examining your code in more detail, here's what it will do:
const express = require('express')
const app = express()
app.get('/', function(req, res){
res.send("main page 1")
})
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware")
})
app.listen(3000)
If you do a GET request for / to your server, Express matches routes in the order declared so the first one that will match is the app.get(). It will send the response with res.send("main page 1") and because it does NOT call next(), all routing will be done and the app.use("/", ...) is never hit.
But, if you typed http://localhost:3000 into the browser, that is not the only request that the browser will send to your server. The browser will also send a request for http://localhost:3000/favicon.ico (the web site icon the browser likes to display).
That request will not be matched by the app.get("/", ...), but because app.use() accepts partial matches (app.get() requires full matches only), the /favicon.ico request will be matched by app.use("/", ..) and you will see your console.log("middleware"). You won't see the results of res.send("main page 2") because when the browser requested the favicon and got back some plain text, it will just ignore that is it clearly isn't the icon it was looking for.
If you modify your middleware to log the actual URL that is being requested, then all should be clear:
app.use("/", function(req, res) {
res.send("main page 2")
console.log("midddleware", req.originalUrl); // log the actual URL
})
See Middleware callback function examples. It shows this example:
var router = express.Router()
router.get('/', function (req, res, next) {
next()
})
If you call next() in your first handler, the next one will get executed. But watch out – you can only call res.send once in a request-response cycle. The second one would throw an error – because the response was already sent.
See also Writing Middleware for more examples:
The order of middleware loading is important: middleware functions that are loaded first are also executed first.
If myLogger is loaded after the route to the root path, the request never reaches it and the app doesn’t print “LOGGED”, because the route handler of the root path terminates the request-response cycle.
The middleware function myLogger simply prints a message, then passes on the request to the next middleware function in the stack by calling the next() function.

What happens if you overload the same route with express js?

I would like to know what happens if I write that in expressjs ( we assume app is an expressjs app).
app.get.use('/here' , function(req, res) {
// EXECUTE SET OF ACTION NUMBER 1
});
app.get.use('/here' , function(req, res) {
// EXECUTE SET OF ACTION NUMBER 2
})
We write the same route twice but with different set of actions. Will the second declaration overload the first one ?
It depends on what's happening inside the callback function. Consider three different examples:
a)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
res.json(true);
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
b)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
c)
const express = require('express');
const app = express();
app.get('/', (req, res, next) => {
console.log('1!');
next();
});
app.get('/', (req, res) => {
console.log('2!');
res.json(true);
});
app.listen(3000);
In the (a) case, the workflow is top-to-bottom. The first route handler kicks in, console.logs "1!" and responds with true. Since the complete response has been sent, the following route handler will never be reached.
In the (b) case, the execution stalls at the first route handler. It neither responds or allows execution to go further. If you curl this app, you'll end up with request timeout, although you'll see "1!" on the console.
Finally, in the (c) case, you can see next function call. This is what makes Express proceed with execution and go to the next route handler, if any. Since there's another route handler, it is executed, so you end up with both "1!" and "2!" printed on the console, as well as the response sent correctly.
A few points here:
always call next if you expect another route handler to run after current one, and it is especially important when you're writing a middleware or, as in your case, a "middleware-like" app that you are possibly going to add to another app,
it's okay to build up response in more than one handler, for example, when the first one declares headers and/or body, and the next one adds something to headers and/or body (which is weird and I'd strongly discourage you from doing so), and
once the response has been sent, the execution stops, and none of the further route handlers can be reached (which isn't guaranteed when you use conditional blocks, for example).

NodeJS fill "req" from other function

I've got a NodeJS + Express Server setup with a router that looks like this:
app.route('/clients/:clientId)
.get(users.ensureAuthenticated, clients.read)
.put(users.ensureAuthenticated, clients.hasAuthorization, clients.update)
.delete(users.ensureAuthenticated, clients.hasAuthorization, clients.delete);
app.param('clientId', clients.clientByID);
My Problem is that users.ensureAuthenticated fills the req parameter with the current user req.user.
Basically it does this: req.user = payload.sub; (with some other background stuff)
Then the req.user is available in the following functions e.g. clients.update, but not in clients.clientByID.
I know I could execute users.ensureAuthenticated in clients.clientByID again, but this would execute the code twice and be extra load on the server, right? I guess there must be another way, but I couldn't find anything in the documentation of express.
I'd like to know how I can access the req.user in clients.clientByID without executing the code in users.ensureAuthenticated twice.
Based on your question, I assume you would like to execute users.ensureAuthenticated before clients.clientByID is executed. This can be achieved by using the app.use functionality. app.use handlers will get executed before the app.param and app.route handlers.
For example:
var express = require('express');
var app = express();
app.use('/user', function(req, res, next) {
console.log('First! Time to do some authentication!');
next();
});
app.param('id', function(req, res, next, id) {
console.log('Second! Now we can lookup the actual user.');
next();
});
app.get('/user/:id', function(req, res, next) {
console.log('Third! Here we do all our other stuff.');
next();
});
app.listen(3000, function() {
});

Running asynchronous code when express server starts

In a previous question I got something like this working.
promiseRoute().bind(app).then(app.use)
What I didn't realize at the time is that this correctly adds the returned route from the promise to app.use however if there is a subsequent app.use after this statement (for instance an error call) expresses router system will run the error routes before it runs the promise returned route.
I'd have to wrap all calls within a promise chain and return app from within a promise which causes more problems when I have to use it.
Is there any system / library / etc for managing this?
I fundamentally want to run a asynchronous function when the server starts and not every time on a specific route. So I need code running above app.use or code within app.use that checks if that async function has run or not. Thoughts?
This is the code I have that jumps right to 404 for my /google request.
var googleRoute = require("./google-route")
googleRoute.on("redirect", function(req, res, next){
return res.redirect(req.googleRedirect)
})
googleRoute.on("tokens", function(req, res, next){
return res.json(req.googleTokens)
})
// returns express route
googleRoute.use({
"toRedirectUrl": "/google",
"appCredentials": path.join(__dirname, "google-tokens.json"),
"genetateAuth": {
"approval_prompt": "force",
"access_type": 'offline',
"scope": "https://www.googleapis.com/auth/drive",
}
}).bind(app).then(app.use)
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
What you want to do here is always keep the app variable at the top level.
let app = express()
app.use(...)
export defaults app
If you want to run something before the app server starts, it should be wrapped around requests that starts the server.
checkIfDatabaseIsUp().then(() => {
let server = http.createServer(app)
})

Require Authentication for directory (except one page) with Passport.js / Node.js?

I'm new to using Passport.js, but I find it's going pretty well so far. I'm using Passport with passport-local.
However, I want to require authentication for an entire directory excluding one page. So in my node server I'm serving up this direcory like so (using express):
app.use("/admin", express.static(__dirname + "/admin"));
And then I want to let the user hit /admin/login.html, so I wanted to do something like this:
app.get('/gb-admin/login.html', function(req, res){ });
Then I want to require authentication for the rest, so something like this:
app.get('/gb-admin/*', ensureAuthenticated, function(req, res){});
Here is my ensureAuthenticated function, for reference if it helps:
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
}
How would I go about doing this? I've been generally sending things in infinite loops and causing the browser to timeout. Can anyone help?
The reason you're getting timeouts is because you can't have an empty route handler; at one point, you have to either return a response, or hand the request over the the next route handler/middleware.
That said, try this:
function ensureAuthenticated(req, res, next) {
if (req.path === '/gb-admin/login.html' || req.isAuthenticated()) {
return next();
}
res.redirect('/gb-admin/login.html')
}
app.get('/gb-admin/*', ensureAuthenticated, function(req, res, next) {
next();
});
// the static middleware needs to be declared after the route above, otherwise
// it will take precedence and ensureAuthenticated will never be called.
app.use("/gb-admin", express.static(__dirname + "/admin"));
I don't think there's a way to get it working with a separate route for the login page (unless you actually implement reading login.html and sending it back from without that routes handler), hence the check for it in the ensureAuthenticated middleware.
I wonder if it is your callback. Try:
app.get('/gb-admin/*', function (req, res, next) {
ensureAuthentication(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/gb-admin/login.html')
});
});

Categories