360

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.

2
  • 1
    Check out express' flash: stackoverflow.com/questions/12442716/… Commented Sep 26, 2013 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. Commented May 21, 2020 at 17:00

10 Answers 10

495

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.

Sign up to request clarification or add additional context in comments.

16 Comments

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...
@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.
Good answer but I think sessions is the better approach than query strings - don't expose stuff in URLs!
@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.
node js has 2 core modules that generate the query string
|
56

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); 

10 Comments

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().
Very good and conservative answer! Much easier and much more secure than the other answers.
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 it will end at /categories unfortunately, so this actually is the same as just rendering the view directly in /categories...
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
|
20

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("/"); } 

2 Comments

No session store is required for express-session. A database just makes sessions persist across server restarts.
I agree to this but however the connection can be intercepted in between redirection, how to deal with that?
10

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')) }); 

3 Comments

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.
or set the user with userId req.app.set('userId', res.req.user)
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.)
10

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); }); 

2 Comments

Hi I tried this but for me the re.session.context is undefined in get api call. stackoverflow.com/questions/72003285/…
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
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("/"); } 

4 Comments

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.
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
req is for a unique user, but req.app is for all users.
use express-session to send the message
4

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) }) 

Comments

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.

3 Comments

As I said in another answer, query strings dont seem good, since I dont know yet how big is the data I want to pass. Your other solution is the more congenial I have found, but it still breaks my intention of keeping the same URL.
@Dbugger Use an AJAX POST possibly?
Not the right way
-2

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.

Comments

-4
 app.get('/category', function(req, res) { var string = query res.redirect('/?valid=' + string); }); 

in the ejs you can directly use valid:

<% var k = valid %> 

1 Comment

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,

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.