Why does this next() function print its argument in console? - node.js

I am a tyro in node and express, learning the things. I was reading Express's documentation at this link: https://expressjs.com/en/guide/routing.html and in the Route Handlers section of this page, it says:
You can provide multiple callback functions that behave like middleware to handle a request. The only exception is that these callbacks might invoke next('route') to bypass the remaining route callbacks.
The examples given on this page doesn't contain any next() with arguments in it.
I tried implementing it and passing some path as an argument in this function but it behaves strangely and prints that argument in the console and also sends it to the browser.
Below is my code:
const express = require('express');
const port = 8000;
const app = express();
app.get('/', (req, res, next) => {
console.log("I am the first one");
next('/demo');
}, function(req, res, next){
console.log("I am the second one");
});
app.get('/demo', (req, res) => {
console.log("Good!");
});
app.listen(port, (err) => {
if(err){
console.log("ERROS: ",err);
}
console.log("Express server is runnig on port: ",port);
});
In the browser, I typed http://localhost:8000/The output in console is:
I am the first one
/demo
the next('/demo') function call doesn't cause /demo route handler to run.
Where am I going wrong?
Am I getting it right?
Actually I am not able to understand how this argument thing works with the next() function.
Since the documentation page does not have any example with arguments in function, Can anyone please explain how this next() function work with arguments through an example?
Thanks in advance for any help you are able to provide

I did some deep diving here because it seemed interesting to me and then I realized it's just the error being returned. Let me explain.
Basically next() which is a expressjs specific functionality is used to pass over the control to next functional unit. It doesn't expect parameters of string, barring one (given below - after the error piece). That even in a case when you initialize it properly.
Here is the output in the network tab once you load the page, please check.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>/demo</pre>
</body>
</html>
NOTE: next('route') will work only in middleware functions that were
loaded by using the app.METHOD() or router.METHOD() functions.
You cannot call another router the way you are doing at the moment, http will have one response for one request to close the channel. However you can do something like.
app.get('/', (req, res, next) => {
console.log("I am the first one");
next();
}, function(req, res, next){
console.log("I am the second one");
next();
});
app.get('/', (req, res, next) => {
console.log("I am the third one");
next();
}, function(req, res, next){
console.log("I am the fourth one");
});
Here middleware works it's magic and matches whatever identical route parameter is and goes on to execute it all.

The next() function in
app.get('/', (req,...
used to call the next callback in the same route
...
function(req, res, next){
console.log("I am the second one");
});
Once you remove the /demo from the next('/demo'), you will notice "I am the second one" in the console. NOTE: next() only passes the control to the next middleware in the same route.
app.get('/', (req, res, next)=>{
// E.g. purpose of this middleware is to print request method
console.log('Request method:', req.method)
next() // this code passes the control to the next callback which is below
}, (req, res) => {
console.log('End of route')
res.send('OK')
}
It does not engage the app's route. In order to go to another route, you have to send another HTTP request. Hope this helps!
UPDATE Here's the scenario where next('route') does something, this only works if 2 routes are of the same URI and http method.
const express = require('express')
const app = express();
function middleware1(req, res, next){
console.log('Middleware #1')
next('route') // ends current callback chain, moves to the next route
}
app.get('/', middleware1, (req, res, next)=>{
console.log('Second middleware') // this callback is not executed
res.send('OK')
})
app.get('/', (req, res)=>{
console.log('Test') // the callback in this route gets called
res.send('2nd get / OK')
})
app.listen(3000, () => {
console.log('Server #', 3000)
})
I think this implementation of route is seldom being put into practice because personally I feel that it is tedious to follow (having 2 routes with the same uri and http method). Nonetheless it is good to learn something from Gandalf the White and this.

Related

I cant use a function returning another function in a express js helper

I have this helper :
function loginRequired(msg){ // this is the helper
return function(req, res, next){
if (req.user.is_authenticated){ // example
return next()
}
//else
req.flash('error_msg',msg)
return res.redirect('/')
}
}
and I have this route ( I`m using the helper in this route ) :
router.post('/new', loginRequired(msg='You are not allowed here'), async (req, res)=>{
// code
})
The problem is that the function returned by the helper is not being executed, when I request this route, it keeps loading forever and the content is never sent.
There isn't anything obviously wrong with the loginRequired() function and how you're using it so the problem is probably elsewhere. The only thing I see is that if req.user doesn't exist, then it would throw.
For further debugging, I would suggest you do this to make sure your route is being matched.
function logRoute(msg) {
return function(req, res, next) {
console.log(msg);
next();
}
}
router.post('/new', logRoute("/new handler"), loginRequired(msg='You are not allowed here'), async (req, res)=>{
// code
});
And, make sure you see /new handler in the console. If you don't even see that, then the problem is further upstream with how your route is declared as it isn't matching an incoming request. You would have to show us the rest of that code for us to see how the router is being used.

How come I can add the second middleware in my NodeJS code without adding next() method

I am just writing a small NodeJS code with express module. It was said that we can't continue our request to next middleware without using next() method. But, I am able to continue the request to next middle ware without using next().
//III Party
const express = require('express');
// returns express app
const app = express();
app.use('/add',(req,res,next)=>{
console.log('Add Middleware');
res.send('Add product');
});
app.use('/delete',(req,res,next)=>{
console.log('delete Middleware');
res.send('Delete Product');
});
// Middle Ware
app.use('/',(req,res,next)=>{
console.log('First Middleware');
res.send('Hello World');
});
app.listen(3000);
Kindly explain me this.This is the output for my "/delete" request which is handled in second middleware(without using next methode in previous middleware)
The output is totally correct.
Your so called "First Middleware" is actually the last middleware.
I guess you misthought the hierarchy of the path determine the order of middlewares, from / then /a then /a/b then /a/b/c... But this is not the case.
In fact, the order you call app.use determines the priority of the middlewares.
Quote from http://expressjs.com/en/api.html#app.use
Middleware functions are executed sequentially, therefore the order of middleware inclusion is important.
// this middleware will not allow the request to go beyond it
app.use(function(req, res, next) {
res.send('Hello World');
});
// requests will never reach this route
app.get('/', function (req, res) {
res.send('Welcome');
});
When you get /delete, it searches for the first middleware that matches the path, that is
app.use('/delete',(req,res,next)=>{
console.log('delete Middleware');
res.send('Delete Product');
});
Since you didn't call next(), any subsequent middleware that matches the path will not be called. i.e.
app.use('/',(req,res,next)=>{
console.log('First Middleware');
res.send('Hello World');
});
is not called.
That's why you see Delete Product in the response text but not Hello World.
You can experiment yourself by changing the order of app.use and adding or removing next() to see different results.

Is there any way to monitor every response in Express.js?

I want to create a middleware function in express.js. which can monitor every requests and responses. I created a middleware but it can only monitor the requests, not the responses.
function middlewareFunc (req,res,next) {
console.log(req.body , req.params , req.query);
next();
}
You should know that res in function(req, res, next) is a instance of class http.ServerResponse. So it can be listen on finish event, please see the link: https://nodejs.org/api/stream.html#stream_event_finish
app.use(function (req, res, next) {
function afterResponse() {
res.removeListener('finish', afterRequest);
res.removeListener('close', afterRequest);
// action after response
}
res.on('finish', afterResponse);
res.on('close', afterResponse);
// action before request
// eventually calling `next()`
});
app.use(app.router);
app.use() and middleware can be used for "before" and a combination of the close and finish events can be used for "after."
For that you can write two middlewares
1) Before all request endpoints.
//middleware
function middlewareFunEarlier(req,res,next) {
console.log(req.body , req.params , req.query);
next();
}
app.use(middlewareFunEarlier);
app.get('/', function(req, res, next){
//do something
res.end();
});
2) After all end points. And you must have to use next() in all endpoints
app.get('/', function(req, res, next){
//do something
next();
});
app.use(middlewareFunLater);
//middlware
function middlewareFunLater(req, res, next){
console.log(res);
res.end();
}
It can be work around with existing tools.
Ok, so first of all, the reason you are only seeing the requests is because of how middleware works. Everything gets run once in a certain order, and runs only once. When your middleware gets run it is most likely before the response has been created. In order to get the response you would have to make your code run when your controller goes to render or something like that.
Second of all, it seems like basic logging is all you need.(weather it be with a library or just console logging stuff.)

Forward request to alternate request handler instead of redirect

I'm using Node.js with express and already know the existence of response.redirect().
However, I'm looking for more of a forward() functionality similar to java that takes the same parameters as redirect, but internally forwards the request instead of having the client perform the redirect.
To clarify, I am not doing a proxy to a different server. I'd like to forward('/other/path') directly within the same app instance
It wasn't apparently obvious how to do this from the express documentation. Any help?
You just need to invoke the corresponding route handler function.
Option 1: route multiple paths to the same handler function
function getDogs(req, res, next) {
//...
}}
app.get('/dogs', getDogs);
app.get('/canines', getDogs);
Option 2: Invoke a separate handler function manually/conditionally
app.get('/canines', function (req, res, next) {
if (something) {
//process one way
} else {
//do a manual "forward"
getDogs(req, res, next);
}
});
Option 3: call next('route')
If you carefully order your router patterns, you can call next('route'), which may achieve what you want. It basically says to express 'keep moving on down the router pattern list', instead of a call to next(), which says to express 'move down the middleware list (past the router)`.
You can implement forward (aka rewrite) functionality by changing request url property and calling next('route').
Note that the handler performing forward needs to be configured before other routes which you perform forwards to.
This is example of forwarding all *.html documents to routes without .html extension (suffix).
function forwards(req, res, next) {
if (/(?:.+?)\.html$/.test(req.url)) {
req.url = req.url.replace(/\.html$/, '');
}
next('route');
}
You call next('route') as the last operation. The next('route') passes control to subsequent routes.
As mentioned above, you need to configure forwards handler as one of the first handlers.
app.get('*', forwards);
// ...
app.get('/someroute', handler);
The above example will return the same content for /someroute as well as /someroute.html. You could also provide an object with a set of forward rules ({ '/path1': '/newpath1', '/path2': '/newpath2' }) and use them in forward mechanism.
Note that regular expression used in forwards function is simplified for mechanism presentation purposes. You would need to extend it (or perform check on req.path) if you would like to use querystring parameters etc.
I hope that will help.
For Express 4+
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
app.get("/a/path", function(req, res){
req.url = "/another/path";
app.handle(req, res);
}
Or for HTML5 mode of React/Angular
const dir = process.env.DIR || './build';
// Configure http server
let app = express();
app.use('/', express.static(dir));
// This route sends a 404 when looking for a missing file (ie a URL with a dot in it)
app.all('/*\.*', function (req, res) {
res.status(404).send('404 Not found');
});
// This route deals enables HTML5Mode by forwarding "missing" links to the index.html
app.all('/**', function (req, res) {
req.url = 'index.html';
app.handle(req, res);
});
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
router.get("/a/path", function(req, res){
req.url = "/another/path";
router.handle(req, res);
}
Express 4+ with nested routers
Instead of having to use the outside of route/function app, you can use req.app.handle
"use strict";
const express = require("express");
const app = express();
//
// Nested Router 1
//
const routerOne = express.Router();
// /one/base
routerOne.get("/base", function (req, res, next) {
res.send("/one/base");
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-within-router -> /one/base
routerOne.get("/redirect-within-router", function (req, res, next) {
req.url = "/base";
next();
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-not-found -> /one/two/base (404: Not Found)
routerOne.get("/redirect-not-found", function (req, res, next) {
req.url = "/two/base";
next();
});
// Using the full URL
// /one/redirect-within-app -> /two/base
routerOne.get("/redirect-within-app", function (req, res, next) {
req.url = "/two/base";
// same as req.url = "/one/base";
//req.url = req.baseUrl + "/base";
req.app.handle(req, res);
});
// Using the full URL
// /one/redirect-app-base -> /base
routerOne.get("/redirect-app-base", function (req, res, next) {
req.url = "/base";
req.app.handle(req, res);
});
//
// Nested Router 2
//
const routerTwo = express.Router();
// /two/base
routerTwo.get("/base", function (req, res, next) {
res.send("/two/base");
});
// /base
app.get("/base", function (req, res, next) {
res.send("/base");
});
//
// Mount Routers
//
app.use("/one/", routerOne);
app.use("/two/", routerTwo);
// 404: Not found
app.all("*", function (req, res, next) {
res.status(404).send("404: Not Found");
});
app.get('/menzi', function (req, res, next) {
console.log('menzi2');
req.url = '/menzi/html/menzi.html';
// res.redirect('/menzi/html/menzi.html');
next();
});
This is my code:when user enter "/menzi",the server will give the page /menzi/html/menzi.html to user, but the url in the browser will not change;
You can use run-middleware module exactly for that. Just run the handler you want by using the URL & method & data.
https://www.npmjs.com/package/run-middleware
For example:
app.runMiddleware('/get-user/20',function(code,body,headers){
res.status(code).send(body)
})

Express next function, what is it really for?

Have been trying to find a good description of what the next() method does. In the Express documentation it says that next('route') can be used to jump to that route and skip all routes in between, but sometimes next is called without arguments. Anybody knows of a good tutorial etc that describes the next function?
next() with no arguments says "just kidding, I don't actual want to handle this". It goes back in and tries to find the next route that would match.
This is useful, say if you want to have some kind of page manager with url slugs, as well as lots of other things, but here's an example.
app.get('/:pageslug', function(req, res, next){
var page = db.findPage(req.params.pageslug);
if (page) {
res.send(page.body);
} else {
next();
}
});
app.get('/other_routes', function() {
//...
});
That made up code should check a database for a page with a certain id slug. If it finds one render it! if it doesn't find one then ignore this route handler and check for other ones.
So next() with no arguments allows to pretend you didn't handle the route so that something else can pick it up instead.
Or a hit counter with app.all('*'). Which allows you to execute some shared setup code and then move on to other routes to do something more specific.
app.all('*', function(req, res, next){
myHitCounter.count += 1;
next();
});
app.get('/other_routes', function() {
//...
});
In most frameworks you get a request and you want to return a response. Because of the async nature of Node.js you run into problems with nested call backs if you are doing non trivial stuff. To keep this from happening Connect.js (prior to v4.0, Express.js was a layer on top of connect.js) has something that is called middleware which is a function with 2, 3 or 4 parameters.
function (<err>, req, res, next) {}
Your Express.js app is a stack of these functions.
The router is special, it's middleware that lets you execute one or more middleware for a certain url. So it's a stack inside a stack.
So what does next do? Simple, it tells your app to run the next middleware. But what happens when you pass something to next? Express will abort the current stack and will run all the middleware that has 4 parameters.
function (err, req, res, next) {}
This middleware is used to process any errors. I like to do the following:
next({ type: 'database', error: 'datacenter blew up' });
With this error I would probably tell the user something went wrong and log the real error.
function (err, req, res, next) {
if (err.type === 'database') {
res.send('Something went wrong user');
console.log(err.error);
}
};
If you picture your Express.js application as a stack you probably will be able to fix a lot of weirdness yourself. For example when you add your Cookie middleware after you router it makes sense that your routes wont have cookies.
Docs
How do I setup an error handler?
Error Handling
You define error-handling middleware in the same way as other middleware, except with four arguments instead of three; specifically with the signature (err, req, res, next):
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
IMHO, the accepted answer to this question is not really accurate. As others have stated, it's really about controlling when next handler in the chain is run. But I wanted to provide a little more code to make it more concrete. Say you have this simple express app:
var express = require('express');
var app = express();
app.get('/user/:id', function (req, res, next) {
console.log('before request handler');
next();
});
app.get('/user/:id', function (req, res, next) {
console.log('handling request');
res.sendStatus(200);
next();
});
app.get('/user/:id', function (req, res, next) {
console.log('after request handler');
next();
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
});
If you do
curl http://localhost:3000/user/123
you will see this printed to console:
before request handler
handling request
after request handler
Now if you comment out the call to next() in the middle handler like this:
app.get('/user/:id', function (req, res, next) {
console.log('handling request');
res.sendStatus(200);
//next();
});
You will see this on the console:
before request handler
handling request
Notice that the last handler (the one that prints after request handler) does not run. That's because you are no longer telling express to run the next handler.
So it doesn't really matter if your "main" handler (the one that returns 200) was successful or not, if you want the rest of the middlewares to run, you have to call next().
When would this come in handy? Let's say you want to log all requests that came in to some database regardless of whether or not the request succeeded.
app.get('/user/:id', function (req, res, next) {
try {
// ...
}
catch (ex) {
// ...
}
finally {
// go to the next handler regardless of what happened in this one
next();
}
});
app.get('/user/:id', function (req, res, next) {
logToDatabase(req);
next();
});
If you want the second handler to run, you have to call next() in the first handler.
Remember that node is async so it can't know when the first handler's callback has finished. You have to tell it by calling next().
next() without parameter invokes the next route handler OR next middleware in framework.
Summarizing rightly mentioned answers in one place,
next() : move control to next function in same route. case of
multiple functions in single route.
next('route') :move control to next route by skipping all remaining
function in current route.
next(err) : move control to error middleware
app.get('/testroute/:id', function (req, res, next) {
if (req.params.id === '0') next() // Take me to the next function in current route
else if (req.params.id === '1') next('route') //Take me to next routes/middleware by skipping all other functions in current router
else next(new Error('Take me directly to error handler middleware by skipping all other routers/middlewares'))
}, function (req, res, next) {
// render a regular page
console.log('Next function in current route')
res.status(200).send('Next function in current route');
})
// handler for the /testroute/:id path, which renders a special page
app.get('/testroute/:id', function (req, res, next) {
console.log('Next routes/middleware by skipping all other functions in current router')
res.status(200).send('Next routes/middleware by skipping all other functions in current router');
})
//error middleware
app.use(function (err, req, res, next) {
console.log('take me to next routes/middleware by skipping all other functions in current router')
res.status(err.status || 500).send(err.message);
});
Question also asked about use of next('route') which seems to be covered week in provided answers so far:
USAGE OF next():
In short: next middleware function.
Extract from this official Express JS documentation - 'writing-middleware' page:
"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."
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)
This page of Express JS documentation states "If the current middleware function does not end the request-response cycle, it must call next() to pass control to the next middleware function. Otherwise, the request will be left hanging."
USAGE OF next('route') :
In short: next route (vs. next middleware function in case of next() )
Extract from this Express JS documentation - 'using-middleware' page:
"To skip the rest of the middleware functions from a router middleware stack, call next('route') to pass control to the next route. NOTE: next('route') will work only in middleware functions that were loaded by using the app.METHOD() or router.METHOD() functions.
This example shows a middleware sub-stack that handles GET requests to the /user/:id path."
app.get('/user/:id', function (req, res, next) {
// if the user ID is 0, skip to the next route
if (req.params.id === '0') next('route')
// otherwise pass the control to the next middleware function in this stack
else next()
}, function (req, res, next) {
// render a regular page
res.render('regular')
})
// handler for the /user/:id path, which renders a special page
app.get('/user/:id', function (req, res, next) {
res.render('special')
})
Its simply means pass control to the next handler.
Cheers
Notice the call above to next(). 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 could be named anything, but by convention, it is always named “next”. To avoid confusion, always use this convention.
next() is the callback argument to the middleware function with req, and res being the http request and response arguments to next in the below code.
app.get('/', (req, res, next) => { next() });
So next() calls the passed in middleware function. If current middleware function does not end the request-response cycle, it should call next(), else the request will be left hanging and will timeout.
next() fn needs to be called within each middleware function when multiple middleware functions are passed to app.use or app.METHOD, else the next middleware function won’t be called (incase more than 1 middleware functions are passed). To skip calling the remaining middleware functions, call next(‘route’) within the middleware function after which no other middleware functions should be called. In the below code, fn1 will be called and fn2 will also be called, since next() is called within fn1. However, fn3 won’t be called, since next(‘route’) is called within fn2.
app.get('/fetch', function fn1(req, res, next) {
console.log("First middleware function called");
next();
},
function fn2(req, res, next) {
console.log("Second middleware function called");
next("route");
},
function fn3(req, res, next) {
console.log("Third middleware function will not be called");
next();
})

Resources