349

I am using express to make a web app in node.js. This is a simplification of what I have:

var express = require('express');
var jade = require('jade');
var http = require("http");


var app = express();
var server = http.createServer(app);

app.get('/', function(req, res) {
    // Prepare the context
    res.render('home.jade', context);
});

app.post('/category', function(req, res) {
    // Process the data received in req.body
    res.redirect('/');
});

My problem is the following:

If I find that the data sent in /category doesn't validate, I would like pass some additional context to the / page. How could I do this? Redirect doesn't seem to allow any kind of extra parameter.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Enrique Moreno Tent
  • 24,127
  • 34
  • 104
  • 189
  • 1
    Check out express' `flash`: http://stackoverflow.com/questions/12442716/nodejs-express-res-redirectback-with-params – tymeJV Sep 26 '13 at 18:05
  • 5
    Terminology matters here: there is no `/` page. There is a `/` route, which express can service, and there is a homepage template, which express can render. If you want to preserve some data for rendering the homepage template after a redirect, the accepted answer notwithstanding, sessions are your friend. They give you a data store that persists across requests and responses _for individual browsing users_ so you can put data in during `/category` handling, and then take it out if it's there during `/` handing. – Mike 'Pomax' Kamermans May 21 '20 at 17:00

10 Answers10

476

There are a few ways of passing data around to different routes. The most correct answer is, of course, query strings. You'll need to ensure that the values are properly encodeURIComponent and decodeURIComponent.

app.get('/category', function(req, res) {
  var string = encodeURIComponent('something that would break');
  res.redirect('/?valid=' + string);
});

You can snag that in your other route by getting the parameters sent by using req.query.

app.get('/', function(req, res) {
  var passedVariable = req.query.valid;
  // Do something with variable
});

For more dynamic way you can use the url core module to generate the query string for you:

const url = require('url');    
app.get('/category', function(req, res) {
    res.redirect(url.format({
       pathname:"/",
       query: {
          "a": 1,
          "b": 2,
          "valid":"your string here"
        }
     }));
 });

So if you want to redirect all req query string variables you can simply do

res.redirect(url.format({
       pathname:"/",
       query:req.query,
     });
 });

And if you are using Node >= 7.x you can also use the querystring core module

const querystring = require('querystring');    
app.get('/category', function(req, res) {
      const query = querystring.stringify({
          "a": 1,
          "b": 2,
          "valid":"your string here"
      });
      res.redirect('/?' + query);
 });

Another way of doing it is by setting something up in the session. You can read how to set it up here, but to set and access variables is something like this:

app.get('/category', function(req, res) {
  req.session.valid = true;
  res.redirect('/');
});

And later on after the redirect...

app.get('/', function(req, res) {
  var passedVariable = req.session.valid;
  req.session.valid = null; // resets session variable
  // Do something
});

There is also the option of using an old feature of Express, req.flash. Doing so in newer versions of Express will require you to use another library. Essentially it allows you to set up variables that will show up and reset the next time you go to a page. It's handy for showing errors to users, but again it's been removed by default. EDIT: Found a library that adds this functionality.

Hopefully that will give you a general idea how to pass information around in an Express application.

Aaron Butacov
  • 32,415
  • 8
  • 47
  • 61
AlbertEngelB
  • 16,016
  • 15
  • 66
  • 93
  • 12
    Querys dont feel really alright to pass context, as this could be as big as a long paragraph, or even more. As for sessions, that may work. but it doesnt really feel right, as this doesnt seem the right purpose of sessions... – Enrique Moreno Tent Sep 26 '13 at 20:33
  • @Dbugger Oops, didn't realize you had asked the question. If you're worried about passing too much information to the user via querystring, it might be better to store the validation data in a DB and then query that when you hit the main `/` route. Another option is to call the post route via AJAX and store the resulting data in localstorage or something similar. – AlbertEngelB Sep 26 '13 at 20:42
  • 18
    Good answer but I think sessions is the better approach than query strings - don't expose stuff in URLs! – user2562923 Feb 27 '14 at 15:48
  • 2
    @user2562923 I agree, sessions or POSTs are better than GETs for passing context around. The answer was pretty vague so I wasn't sure what sort of info the asker was passing around. – AlbertEngelB Feb 27 '14 at 16:19
  • node js has 2 core modules that generate the query string – Fareed Alnamrouti May 23 '17 at 04:59
  • @fareednamrouti Not sure what your point is? `querystring.stringify()` is a valid option, but IIRC this answer predates that method being available. If you'd like to edit my answer to use these newer methods, feel free. :) – AlbertEngelB May 23 '17 at 18:13
  • You are right but I guess it's more practical to use one of these modules :) – Fareed Alnamrouti May 23 '17 at 22:31
  • honestly I don't agree with the session option since it's not the purpose of session to do so and from my experience this can lead to unexpected behaviors but still it's an option :p – Fareed Alnamrouti May 23 '17 at 22:54
  • @fareednamrouti I agree for the most part there. When I originally answered it I tried to cover all the options I could think of. I do think sessions have a use-case for long-lasting information you may need to expose, but I concede that it's probably better to use the other methods if you can. – AlbertEngelB May 24 '17 at 18:15
  • i see anw it has some use cases – Fareed Alnamrouti May 24 '17 at 18:21
  • Just letting you know... your "[You can read how to set it up here](http://blog.modulus.io/nodejs-and-express-sessions)" link is broken. >> The DNS cannot be found. – Sometowngeek Nov 19 '17 at 20:40
  • I recommend using [\[Node Web Apps\] - How do Express.js Sessions work?](https://nodewebapps.com/2017/06/18/how-do-nodejs-sessions-work/) to understand how sessions work in express.js – Sometowngeek Nov 19 '17 at 21:19
  • The session concept is a nice thought that hadn't occurred to me! Great answer for that! – Nikhil Eshvar Jul 02 '19 at 07:10
  • I liked the session hack... Bravo – Omer Sep 15 '22 at 18:55
  • wayback url of the broken link : https://web.archive.org/web/20161119044954/http://blog.modulus.io/nodejs-and-express-sessions – Spidy Nov 28 '22 at 10:10
  • Just FYI the session way isn't much different from query strings. Both are public and are a subject to payload limits (albeit different ones). Also if you interpolate session values into a template, then you can say bye-bye to http cache, since the response bodies will be different for the same url. – Biller Builder Nov 30 '22 at 15:13
54

The easiest way I have found to pass data between routeHandlers to use next() no need to mess with redirect or sessions. Optionally you could just call your homeCtrl(req,res) instead of next() and just pass the req and res

var express  = require('express');
var jade     = require('jade');
var http     = require("http");


var app    = express();
var server = http.createServer(app);

/////////////
// Routing //
/////////////

// Move route middleware into named
// functions
function homeCtrl(req, res) {

    // Prepare the context
    var context = req.dataProcessed;
    res.render('home.jade', context);
}

function categoryCtrl(req, res, next) {

    // Process the data received in req.body
    // instead of res.redirect('/');
    req.dataProcessed = somethingYouDid;
    return next();
    // optionally - Same effect
    // accept no need to define homeCtrl
    // as the last piece of middleware
    // return homeCtrl(req, res, next);
}

app.get('/', homeCtrl);

app.post('/category', categoryCtrl, homeCtrl);
jqualls
  • 1,483
  • 14
  • 19
  • 4
    Buddy, I love this answer. Helped me out of a hole there - though I really need to do more reading as my gut was crying out for me to pass req and res in as arguments for next(). – JonRed Nov 02 '14 at 05:26
  • all next does is call the next req handler. If you pass anything to it the function signature will change and it will execute an error handler if one exists. – jqualls Feb 11 '15 at 21:17
  • Thanks for the great answer. :). It would be awesome if you could even break the 'Ctrl' functions in different files. – sum2000 Aug 31 '15 at 05:59
  • 1
    Very good and conservative answer! Much easier and much more secure than the other answers. – Triforcey May 25 '16 at 00:41
  • 2
    This approach seems to be the cleaner, but I'm not sure if it will left the address bar in the latest path or if it will end at `/` – Danielo515 May 27 '16 at 10:55
  • @sum2000 It's quite easy to have the 'xxxCtrl' functions in separate files; `homeCtrl.js` would define `function homeCtrl(...)` as normal and end `module.exports = homeCtrl ;` etc., then the "routing" file would contain a collection of `app.get('/', require('./homeCtrl.js'));` lines. – TripeHound Aug 12 '16 at 15:41
  • 1
    @Danielo515 it will end at /categories unfortunately, so this actually is the same as just rendering the view directly in /categories... – Manuel Graf May 09 '17 at 14:49
  • 3
    I don't agree you are just using the router as a service to render deferent view which will leave the URL as it's, it is bad idea to structure your code that way, the purpose of next is moving to the next middleware and we usually isolate our business logic from the routers logic – Fareed Alnamrouti May 23 '17 at 23:01
  • If you just want to rerender the existing page, this solution is perfectly acceptable and safe. Just be sure to update the context of other variables on that page too if they have changed in the meanwhile because calling `next()` simply continues the request instead of starting a new one. – Jean-Paul Apr 02 '20 at 07:49
  • Agree with @FareedAlnamrouti, I would avoid this - it's not RESTful to eliminate the redirects and serve up different content under the same URL. – Yarin Jan 16 '22 at 19:02
18

I had to find another solution because none of the provided solutions actually met my requirements, for the following reasons:

  • Query strings: You may not want to use query strings because the URLs could be shared by your users, and sometimes the query parameters do not make sense for a different user. For example, an error such as ?error=sessionExpired should never be displayed to another user by accident.

  • req.session: You may not want to use req.session because you need the express-session dependency for this, which includes setting up a session store (such as MongoDB), which you may not need at all, or maybe you are already using a custom session store solution.

  • next(): You may not want to use next() or next("router") because this essentially just renders your new page under the original URL, it's not really a redirect to the new URL, more like a forward/rewrite, which may not be acceptable.

So this is my fourth solution that doesn't suffer from any of the previous issues. Basically it involves using a temporary cookie, for which you will have to first install cookie-parser. Obviously this means it will only work where cookies are enabled, and with a limited amount of data.

Implementation example:

var cookieParser = require("cookie-parser");

app.use(cookieParser());

app.get("/", function(req, res) {
    var context = req.cookies["context"];
    res.clearCookie("context", { httpOnly: true });
    res.render("home.jade", context); // Here context is just a string, you will have to provide a valid context for your template engine
});

app.post("/category", function(req, res) {
    res.cookie("context", "myContext", { httpOnly: true });
    res.redirect("/");
}
cprcrack
  • 17,118
  • 7
  • 88
  • 91
  • 2
    No session store is required for `express-session`. A database just makes sessions persist across server restarts. – Mike 'Pomax' Kamermans May 21 '20 at 17:03
  • I agree to this but however the connection can be intercepted in between redirection, how to deal with that? –  May 08 '23 at 14:37
9

use app.set & app.get

Setting data

router.get(
  "/facebook/callback",
  passport.authenticate("facebook"),
  (req, res) => {
    req.app.set('user', res.req.user)
    return res.redirect("/sign");
  }
);

Getting data

router.get("/sign", (req, res) => {
  console.log('sign', req.app.get('user'))
});

U.A
  • 2,991
  • 3
  • 24
  • 36
  • 5
    bad idea to implement it. it can affect all the user all req.app is same for all users. we can use **express-session** instead of this. – ujjal das Mar 10 '20 at 15:01
  • or set the user with userId `req.app.set('userId', res.req.user)` – U.A Sep 08 '20 at 10:44
  • 2
    Absolutely don't do this - misuse of singleton values/stores (like Express's app object) in HTTP servers is probably the most common cause of security vulnerabilities I've seen, and this is a great demo of one. Imagine two users of this system, one user slightly ahead in the signin flow vs. the other: the first user just hit GET /sign, while the second user is at GET /facebook/callback, prior to redirect to /sign. The first user's GET is then logging the second user's information. Whoops. Thank goodness it's only logging. If it was keyed by unique use, it'd work (that's what sessions are.) – waded Oct 31 '22 at 13:00
9

we can use express-session to send the required data

when you initialise the app

const express = require('express');
const app = express();
const session = require('express-session');
app.use(session({secret: 'mySecret', resave: false, saveUninitialized: false}));

so before redirection just save the context for the session

app.post('/category', function(req, res) {
    // add your context here 
req.session.context ='your context here' ;
    res.redirect('/');
});

Now you can get the context anywhere for the session. it can get just by req.session.context

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

    // So prepare the context
var context=req.session.context;
    res.render('home.jade', context);
});
ujjal das
  • 897
  • 11
  • 15
  • Hi I tried this but for me the re.session.context is undefined in get api call. https://stackoverflow.com/questions/72003285/passport-req-session-passport-always-gives-undefined-on-other-api-calls – user1187 May 12 '22 at 07:44
  • If you choose to use this method, remember to set the session variable to `null` after `res.redirect()` so the error doesn't stick around on page refresh, or another attempt – whoff Aug 02 '22 at 03:58
6

Here s what I suggest without using any other dependency , just node and express, use app.locals, here s an example :

    app.get("/", function(req, res) {
        var context = req.app.locals.specialContext;
        req.app.locals.specialContext = null;
        res.render("home.jade", context); 
        // or if you are using ejs
        res.render("home", {context: context}); 
    });

    function middleware(req, res, next) {
        req.app.locals.specialContext = * your context goes here *
        res.redirect("/");
    }
Hussein Dimessi
  • 377
  • 3
  • 8
  • 4
    I feel like this is a bad idea because req.app.locals seems to affect all users, not just the one that made the request. Sure you will clear the data as soon as it's passed to the user, but I'd imagine you'd still have it conflicting and showing incorrect info from time to time. And if you have to clear it anyway you might as well just store it in the session instead. – stackers Apr 17 '19 at 22:34
  • 1
    actually it will only effect the request the sent by the "unique" users, even if 2 or more users send requests at the "same time" each user will have his own request object and its locals object, that way it s completely safe to use – Hussein Dimessi Apr 19 '19 at 09:34
  • 1
    req is for a unique user, but req.app is for all users. – ZeroCho Dec 22 '19 at 05:54
  • 1
    use **express-session** to send the message – ujjal das Mar 10 '20 at 15:02
3

You can pass small bits of key/value pair data via the query string:

res.redirect('/?error=denied');

And javascript on the home page can access that and adjust its behavior accordingly.

Note that if you don't mind /category staying as the URL in the browser address bar, you can just render directly instead of redirecting. IMHO many times people use redirects because older web frameworks made directly responding difficult, but it's easy in express:

app.post('/category', function(req, res) {

  // Process the data received in req.body

  res.render('home.jade', {error: 'denied'});
});

As @Dropped.on.Caprica commented, using AJAX eliminates the URL changing concern.

Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
2

Update 2021: i tried url.format and querystring and both of them are deprecated, instead we can use URLSearchParams

const {URLSearchParams} = require('url')
               
app.get('/category', (req, res) =>{
    const pathname = '/?'
    const components ={
     a:"a",
     b:"b"
    }

    const urlParameters = new URLSearchParams(components)
    res.redirect(pathname + urlParameters)
})
Dumitru Birsan
  • 719
  • 7
  • 13
-1

I use a very simple but efficient technique in my app.js ( my entry point ) I define a variable like

let authUser = {};

Then I assign to it from my route page ( like after successful login )

authUser = matchedUser

It May be not the best approach but it fits my needs.

mercury
  • 2,390
  • 3
  • 30
  • 39
-3
 app.get('/category', function(req, res) {
  var string = query
  res.redirect('/?valid=' + string);
});

in the ejs you can directly use valid:

<% var k = valid %>
  • Also to be noted, the line, ‘‘‘app.get('/', function(req, res) { // Prepare the context res.render('home.jade', context); });``` should be last as a catchall, – Robertino Vasilescu Aug 11 '20 at 06:47