How does node.js next() middleware function work without arguments? - javascript

Middleware functions have a signature function (req, res, next), but in Express the next() call does not contain arguments. How is this so? See the following example from the
sample documentation
var express = require('express')
var app = express()
var myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000)
It certainly could be the case that a wrapping function is created that under-the-hood binds the arguments, allowing for a call with no additional parameters, but the documentation seems to indicate that the next parameter is called as-is, which does not make sense.

The docs describe the third argument, conventionally named next, as
Callback argument to the middleware function, called "next" by convention.
You can think of it similar to the conventional node.js callback-style argument provided to most async functions (without promises). When your middleware function is done doing its sync or async work, it should call next to indicate to the express router that it is done executing. This argument could be called done or callback, as we often see in other node.js libraries and examples, but is called next to provide a hint to the developer that the middleware chain will continue execution (other middleware may be called after this one).

Related

Is it advisable to use the Express app.use() method call within an async callback?

My question begs a recommendation on best practice.
I wrote a simple Express server that creates a JWT and sends it to the client whenever it (the client) hits an API route.
I have kept the route handler in a separate module and exported it from there because I want to keep the code as modular as possible. I placed the app.use() call that utilizes the middleware for that route within the callback of signJWT() and though this works, I need to know if this is good practice. I did this because the response to be sent back needs the JWT to be signed and ready and this itself is an asynchronous operation. If this isn't good way to go about it, that is placing an app.use() call within an async callback, I would like to know a better way to do so without having to declare my route handler in the same file as my server entry file (app.js) code.
There are 2 main files to examine here; app.js and routes.js. I have included code from a third file - my client-side JavaScript file (script.js) just for context.
1. app.js (Server + Application-wide middleware usage):
const express = require("express");
const app = express();
const path = require("path");
// JWT Library
const jose = require("jose");
// library for generating symmetric key for jwt
const { createSecretKey } = require("crypto");
// Route handler
const routes = require("./routes");
// Create key
const secretKey = createSecretKey("MySecret", "utf-8");
// Sign the token
async function signJWT() {
const jwt = await new jose.SignJWT({
"urn:example:claim": true,
})
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("1h")
.sign(secretKey);
return jwt;
}
// Expose static content
app.use(express.static(path.join(__dirname, "public")));
// This works but is this okay?
signJWT().then((jwt) => app.use("/login", routes({ jwt: jwt })));
app.listen(81);
console.log("Listening on port 81");
The callback in question:
Should app.use() even be in callbacks?
// This works but is this okay?
signJWT().then((jwt) => app.use("/login", routes({ jwt: jwt })));
I initially tried to do this, but values from the Async operation in signJWT() weren't ready and understandably so:
app.use("/login", routes({ jwt: signJWT() }));
Of course, this works - I used it initially, but I don't want to clutter app.js with route handler declarations:
app.get("/login", async function (req, res) {
const jwt = await signJWT();
res.json({ jwt: jwt });
});
2. routes.js (Route Handler file):
const express = require("express");
const router = express.Router();
const wrapper = function (route_params) {
router.get("/", function (req, res) {
res.json(route_params);
});
return router;
};
module.exports = wrapper;
3. script.js (Client JavaScript):
document.getElementById("btn").onclick = function () {
makeRequest("/login");
};
function makeRequest(url) {
fetch(url, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((res) => res.json())
.then((json) => console.log(json));
}
Sample output (in Client's browser console):
{jwt: 'eyJhbGciOiJIUzI1NiJ9.eyJ1cm46ZXhhbXBsZTpjbGFpbSI6d…Dk3fQ.0C_n25RPQNAP-wxy9PHmpo4SRtXBiiHn0fxGPspqxQM'}
Even though this works, placing an app.use() call within an async callback feels hacky to me and it looks like something that could cause problems later on, to me at least, because Express' execution of top-level code is contingent on an async callback.
Is there a better way to declare this call and pass the async value from signJWT() to it so that it will be available in the route handler?
Thank you.
This statement:
signJWT().then((jwt) => app.use("/login", routes({ jwt: jwt })));
is fine. But, what it means is that the /login route handler will not be defined immediately and your server will actually startup initially without that route defined. Though that is unlikely to cause much of a problem in the real world, it would be technically safer to not start your server until after this asynchronous operation finishes like this:
signJWT().then((jwt) => {
app.use("/login", routes({ jwt: jwt }));
app.listen(81);
console.log("Listening on port 81");
});
Is it advisable to use the Express app.use() method call within an async callback?
That is not a common way to write Express code, but there's nothing technically wrong with it. If setting up your route definitions depend upon some asynchronous operation and you don't have access to top level await, then this is one of your options. The other options would be to gather all your asynchronous info first and then define all the routes together inside of one main callback. Then, it feels more natural as you define all the routes sequentially in one block of code.
This:
app.get("/login", async function (req, res) {
const jwt = await signJWT();
res.json({ jwt: jwt });
});
works quite differently. This calls signJWT() on each /login request whereas the previous calls it only once upon startup. So, you need to decide which architecture you want and go with that version of the code. The first code block above calls signJWT() only once upon server startup. This version calls it for every /login request.
Is it advisable to use the Express app.use() method call within an async callback?
That is perfectly fine. Express pays no attention at all to the return value from the app.use() callback so it doesn't matter to it whether you're returning a promise or returning nothing or returning some random value. Likewise, Express doesn't do anything when the callback returns either so it doesn't matter that it doesn't wait for that returned promise to resolve. Express only continues with route matching when you manually call next(), so again it's just ignoring the promise the async function returns (and that's fine). So, if your code works more easily by using an async callback, that's perfectly fine. You do need to know that your handler is entirely responsible for catching it's own rejections because Express will not catch them for you. Some people use a wrapper with Express to catch rejections (and turn them into 5xx error statuses).

Question about the next() function in expressjs

I'm having trouble wrapping my head around the concept of the next() function in express.js. I guess my first question would be is next() an express.js only function? My second question would be, in the example below what does next do? After the console function, it goes to the next function that is called after? I'm so confused.
var cb0 = function (req, res, next) {
console.log('CB0');
next();
}
With Express (and other similar systems), each request passes through a series of middleware functions (like your cb0). Each of those has a chance to do something with the request.
Since the thing a middleware function does may be asynchronous (for instance, reading a file, querying a database, etc.), Express can't just directly call the next bit of middleware after calling the previous one. So instead, it passes the middleware function a function, next, which that middleware uses to say "I'm done, run the next step." (In the Express version, you can also pass an argument to next, as Aikon Mogwai points out: If you pass it an Error, it triggers error handling for the route. If you pass it "route", it jumps to the next router, etc.).
So the concept of a next function isn't specific to Express, but the specific use in that example is.
Here's a much simplified example not using Express, but demonstrating the sort of thing it does with middleware functions when handling a request:
const app = {
middleware: [],
use(callback) {
this.middleware.push(callback);
}
};
app.use((req, res, next) => {
console.log("First handler synchronous part");
setTimeout(() => {
console.log("First handler async part finished");
next();
}, 800);
});
app.use((req, res, next) => {
console.log("Second handler is entirely synchronous");
next();
});
app.use((req, res, next) => {
console.log("Third handler synchronous part");
setTimeout(() => {
console.log("Third handler async part finished");
next();
}, 800);
});
// Code handling an incoming request
function handleRequest(req, app) {
// Copy the handlers
const middleware = app.middleware.slice();
// Create a "response"
const res = {};
// Call the handlers
let index = 0;
next();
function next() {
if (index < middleware.length) {
// Call the handler, have it call `next` when it's done
middleware[index++](req, res, next);
} else {
console.log("Request completed");
}
}
}
handleRequest({}, app);
It's probably worth mentioning that this manual-style of asynchronous middleware handling has been replaced with promises in Koa.js, which is a new(er) framework from the same people who did Express.js. With Koa, you make your callbacks async functions, and Koa's internals wait for the promise the async function returns to settle and then acts on the result of it setting (e.g., rejection or fulfillment, the value it fulfills with, etc.).
Next is used to pass control to the next middleware function. If not the request will be left hanging or open. Calling this function invokes the next middleware function in the app. The next() function is not a part of the Node.js or Express API but is the third argument that is passed to the middleware function.
The next() function requests the next middleware function in the application. The next() function is not a part of the Node.js or Express API, but it is the third case/argument which is passing to the middleware function. The next() function could be named anything, but by convention, it is always named "next". To avoid confusion, always use this convention.
For more info, you can visit the official tutorial of express
var express = require('express')
var app = express()
var CB0 = function (req, res, next) {
console.log('CB0')
next()
}
app.use(CB0)
app.get('/', function (req, res) {
res.send('Hello World!')
})
app.listen(3000)
Each and Every time app receives a request and prints the message "CB0" console in terminal window.
The middleware functions that are loaded first are also executed first.
The middleware function CB0 simply prints a message, then passes on the request to the next middleware function in the stack by calling the next() function.

Why do we use next() function?

Do we use it only in middlewares or the functions with no routes? I have used it in my authentication file...
function ensureAuthenticated(req, res, next){
if(req.isAuthenticated()){
return next();
} else {
req.flash('error_msg' , 'Please login First...')
res.redirect('/users/login');
}
}
You execute it in order to execute the next middleware in the pipe. If you don't execute it manually, you're probably passing it on to a different module that executes it for you. Otherwise, if you don't execute next, you cut the "pipe" short i.e. yours is the last middleware that runs.
In your case it makes perfect sense to call next() when the user is authenticated. That passes the control to the next piece of middleware. It also makes sense not to call next() in the case that you've already established that the user is not authenticated, since in most cases you don't want the rest of the middleware in the "pipe" to execute.
So yes, next() is used by middleware, or modules intended to be called by middleware.
I hope that answers your question.
If you talking about Express framework here - next function is just a callback telling that this particular middleware handler should execute next operation in Express middleware chain.
If you define any handler with signature someHandler(req, res, next) and register it with app.use, it will become a part of that chain. Essentially it just a special callback function - disregarding the purpose of the function itself - route controller, authentication, body parser, etc.

How does this Node.js middleware work without arguments?

I'm using a function that someone else wrote for express and passport, which defines the middleware(?) as follows:
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()){
return next();
}
else{
req.flash('error', 'You need to be logged in to access this page');
res.redirect('/login');
}
}
This function is used in the router as follows:
app.get('/page', isLoggedIn, function(req, res){
// ...
});
What I don't understand is, shouldn't the function be called with parameters req and res? Maybe the callback next is not necessary since it's the next argument of app.get, but how does the function access req and res? I would expect it to be called as follows:
app.get('/page', isLoggedIn(req, res), function(req, res){
// ...
});
How does it work without specifying the arguments?
Thanks,
Any functions that you pass to app.get() or app.use() are automatically called with req, res, next passed to them. That is how app.get() and app.use() are implemented.
To help you understand, this example:
app.get('/page', function(req, res){
console.log(req.params.foo);
});
is functionally the same as this:
app.get('/page', myHandler);
function myHandler(req, res) {
console.log(req.params.foo);
});
You do not want to do something like this:
app.get('/page', isLoggedIn(req, res), function(req, res){
// ...
});
because here you're attempting to execute isLoggedIn(req, res) (when req and res are not yet defined) and then pass it's returned value to app.get(). That is not what you want at all. You need to pass a function reference to app.get() and it will supply the parameters when it calls the function. Any time you put () after a function in Javascript, that means you want to execute it NOW. But, if you just pass the function's name, then that is a function reference which can be stored and called later as desired.
This code example is analogous to this:
var result = isLoggedIn(req, res);
app.get('/page', result, function(req, res){
// ...
});
Besides the fact that this would cause an error because req and res are not defined at program start time when this would execute, hopefully you can see that you don't want to execute isLoggedIn() now. Instead, you just want to pass a function reference so Express can call it later.
In this code
app.get('/page', isLoggedIn, function(req, res){
// ...
});
The app.get method is being called with three arguments:
the route to the page: /page
the middleware function
the request handler function
Basically, this code is telling the express framework that when a GET request is received for the /page path, then it should call two functions: first, the middleware function and second the handler function.
The important thing to note here is that it is the framework doing the work. The framework is going to call the middleware function, then it's going to call the handler function.
What I don't understand is, shouldn't the function be called with parameters req and res?
It will be called with these arguments, somewhere inside the get function. Suppose this is the simplified get, implemented as
// a super simple get that will expect a function
// and call the function with two arguments
function get( f ) {
var res = 1, req = 1;
f( res, req );
}
There are multiple ways of passing a function to get. For example, you pass an anonymous function
get( function( res, req ) {
return res + req;
}
You can also pass a named function defined elsewhere
function isLoggedIn( res, req ) {
return res + req;
}
get( isLoggedIn );
This however, is not what you'd expect:
get( isLoggedIn( res, req ) );
The problem here is that
isLoggedIn( res, req )
is no longer a function declaration. It is an invocation and the result of this invocation depends on what res and req are. With some luck, the invocation can even yield a number value, which means that get is no longer invoked with function as an argument but with the result of function invocation.
In short, to pass a named function to another function, you don't specify its arguments. The supposed syntax that would allow this doesn't even make sense because it would be indistinguishable from a syntax of actual function invocation (i.e. the value of the call).

app.get() third parameter in Node.js with Express

I am working through an excellent tutorial on using the Node.js package Passport (link) for user authentication, and I ran into a piece of code that I really don't understand:
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profile.ejs', {
user : req.user // get the user out of session and pass to template
});
});
My question is with the isLoggedIn parameter. I looked at the official site, and did some google searches, but nowhere does it say that you can pass three parameters into app.get. I've only ever seen two. What is this third (optional, I assume) parameter?
I'm not asking about the isLoggedIn itself, but rather, about the fact that it's a third parameter I've never seen passed into app.get() before.
It's called middleware, and it's called before the third parameter (the callback).
Middleware functions examples: access checks, check to see if user is logged in before passing resources, and such.
It's in the express documentation: http://expressjs.com/en/5x/api.html#app.get
The syntax is:
app.get(path, callback [, callback ...])
i.e.
app.get(path, ...callback)
The syntax includes taking in a path as the first parameter, followed by as many middleware (having access to the request and response) callback functions as you desire. It's not limited to one. They are asynchronous and chained together by calling the next() parameter.
function callbackOne(req, res, next) {
//some code
next();
}
function callbackTwo(req, res, next) {
//some code
res.render();
}
app.get(path, callbackOne, callbackTwo)
So, in your case, the isLoggedIn parameter is simply just another middleware function that eventually calls next() if the user is logged in to bring execution to the third parameter.

Categories

Resources