Skip to content

Instantly share code, notes, and snippets.

@brianmacarthur
Last active July 10, 2023 18:41
Show Gist options
  • Save brianmacarthur/a4e3e0093d368aa8e423 to your computer and use it in GitHub Desktop.
Save brianmacarthur/a4e3e0093d368aa8e423 to your computer and use it in GitHub Desktop.
Flash messaging in Express 4: express-flash vs. custom middleware in ejs, handlebars, or jade

Flash Messages

Flash messaging in Express can be confusing to the newcomer. I know because I am a newcomer. The confusion tends to arise from the distinction between the concept of flash messaging, a temporary message from the server available to templates, and the various implementations of flash messaging, which include express-flash and other modules. This Gist provides a minimal example of 2 approaches to flash messaging, the express-flash module and custom middleware, using 3 common templating engines.

The express-flash module exposes getter and setter methods for a flash message of the form, { flash: { type: 'type', message: 'message' }} and depends on the express-session module. The method req.flash(type, message) sets the value of a new flash message and adds it to an array of messages of the same type. The method req.flash(type) gets the value of all flash messages matching of the same type and returns either a string value for the message if there is only 1 of that type, or an array of string values for messages matching that type. The flash messages have a lifetime of one request and are deleted afterward.

The custom middleware is lifted with modifications from Ethan Brown's excellent book, Web Development with Node & Express, and depends on the express-session module. In it, res.locals.sessionFlash acts as a the getter and req.session.sessionFlash acts as the setter for messages with a lifetime of one request. As in the express-flash module, these flashes take the form, { type: 'type', message: 'message' }, but only do so to follow established convention. They could as easily take the form, { class: 'class', intro: 'intro', text: 'text' } and be exposed via res.locals.myFlashMessages and req.session.myFlashMessages.

There are a couple of important distinctions between the 2 approaches illustrated above.

First, the custom middleware option exposes all of the properties of the flash object, not just the message. So sessionFlash.type and sessionFlash.message are both available to the templates above just by including the sessionFlash value, whereas the expressFlash message type would have to have been explicitly specified in app.js and passed as a separate value, ie expressFlashType, to the template.

Second, the custom middleware does not include a mechanism for adding multiple messages of the same type to an array. As it is written, res.locals.sessionFlash and req.session.sessionFlash are single occupancy variables.

Of note, res.locals.flash and req.session.flash can be used in the custom middleware above, but I chose different targets to avoid collision with the express-flash messages.

Both approaches offer simple mechanisms for passing temporary messages to the browser. Which one of the many options you choose depends on your needs. I hope this has been helpful.

var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var flash = require('express-flash');
var handlebars = require('express-handlebars')
var app = express();
var sessionStore = new session.MemoryStore;
// View Engines
app.set('view engine', 'jade');
//app.engine('handlebars', handlebars()); app.set('view engine', 'handlebars');
//app.set('view engine', 'ejs');
app.use(cookieParser('secret'));
app.use(session({
cookie: { maxAge: 60000 },
store: sessionStore,
saveUninitialized: true,
resave: 'true',
secret: 'secret'
}));
app.use(flash());
// Custom flash middleware -- from Ethan Brown's book, 'Web Development with Node & Express'
app.use(function(req, res, next){
// if there's a flash message in the session request, make it available in the response, then delete it
res.locals.sessionFlash = req.session.sessionFlash;
delete req.session.sessionFlash;
next();
});
// Route that creates a flash message using the express-flash module
app.all('/express-flash', function( req, res ) {
req.flash('success', 'This is a flash message using the express-flash module.');
res.redirect(301, '/');
});
// Route that creates a flash message using custom middleware
app.all('/session-flash', function( req, res ) {
req.session.sessionFlash = {
type: 'success',
message: 'This is a flash message using custom middleware and express-session.'
}
res.redirect(301, '/');
});
// Route that incorporates flash messages from either req.flash(type) or res.locals.flash
app.get('/', function( req, res ) {
res.render('index', { expressFlash: req.flash('success'), sessionFlash: res.locals.sessionFlash });
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Flash app listening at http://%s:%s', host, port);
});
module.exports = app;
<h3> Flash messages in Express 4 </h3>
<p><a href="http://localhost:3000/express-flash">Click here</a> for a flash message using the express-flash module.</p>
<p><a href="http://localhost:3000/session-flash">Click here</a> for a flash message using custom middleware</p>
<p>Refresh to clear the flash message.</p>
<% if ( expressFlash.length > 0 ) { %>
<p><strong>FLASH!</strong> <%= expressFlash %></p>
<% } %>
<% if ( sessionFlash && sessionFlash.message) { %>
<div class="<%= sessionFlash.type %>">
<strong>FLASH!</strong> <%= sessionFlash.message %>
</div>
<% } %>
<h3> Flash messages in Express 4 </h3>
<p><a href="http://localhost:3000/express-flash">Click here</a> for a flash message using the express-flash module.</p>
<p><a href="http://localhost:3000/session-flash">Click here</a> for a flash message using custom middleware</p>
<p>Refresh to clear the flash message.</p>
{{#if expressFlash}}
<p><strong>FLASH!</strong> {{ expressFlash }}</p>
{{/if}}
{{#if sessionFlash.message}}
<div class="{{ sessionFlash.type }}">
<strong>FLASH!</strong> {{ sessionFlash.message }}
</div>
{{/if}}
block content
h3 Flash messages in Express 4
p <a href="http://localhost:3000/express-flash">Click here</a> for a flash message using the express-flash module.
p <a href="http://localhost:3000/session-flash">Click here</a> for a flash message using custom middleware
p Refresh to clear the flash message.
if expressFlash.length > 0
p <strong>FLASH!</strong> #{ expressFlash }
if sessionFlash && sessionFlash.message
div(class=sessionFlash.type)
p <strong>FLASH!</strong> #{ sessionFlash.message }
{
"dependencies": {
"cookie-parser": "~1.3.3",
"express": "~4.11.1",
"express-flash": "0.0.2",
"express-session": "^1.10.1",
"jade": "~1.9.1",
"express-handlebars": "1.1.0",
"ejs": "2.2.3"
}
}
@jrh
Copy link

jrh commented Mar 25, 2016

so helpful, thanks!

@Biouche
Copy link

Biouche commented Mar 28, 2016

Nice snippets, clear and usefull. Thx !

@olefrank
Copy link

Cannot get it to work. Which example are you using? 1, 2 or 3?

@danejordan
Copy link

Worked once I moved the views into a created views folder! Thanks!

@pz7gc3
Copy link

pz7gc3 commented Dec 11, 2016

Thanks for sharing! I'm new to handlebars myself and found the flash stuff confusing.
With the help of Your sample I got it to work.

@tmanolat
Copy link

Very thorough! Thanks!

@wattry
Copy link

wattry commented Jan 29, 2018

Thanks so much, maybe you could submit a pull request with this as a readme.md change to the repo!

@rayraydejesus
Copy link

OMG I LOVE YOU!

@muarachmann
Copy link

thanks a lot

@samsher
Copy link

samsher commented Apr 28, 2018

not work for me. Please help me

@ltanase77
Copy link

It should be outlined that both approaches are based on the session mechanism. If you destroy the session (as when the users logs out) you will not be able to pass flash messages to the redirected view.

@namsir
Copy link

namsir commented Dec 25, 2018

anyone got a bug where the first time you start the app, set the message in your post route, redirect to get route and it's not there. Do the post again, it shows 2 messages in the array. Post again, now it's working correctly. Is it a bug or something?

Here is my method in my controller for POST:

exports.save = function(req, res) {
  const email = req.body.email;
  const activated = parseInt(req.body.activate); // 1 or 0 as string
  console.log(email);
  User.findOne({ email: email })
    .then(user => {
      console.log(activated);

      if (activated === 1) {
        user.registered = true;
      } else {
        user.registered = false;
      }
      return user.save();
    })
    .then(result => {
      //   console.log(activated);
      const msg = `User with an email of ${email} has been ${
        activated ? "activated" : "deactivated"
      }.`;
      console.log(msg);

      req.flash("adminupdate", msg);
      res.redirect("/admin");
    })
    .catch(err => {
      console.log(err);
    });
};

Here is the method for GET:

exports.index = function(req, res) {
  User.find().then(users => {
    // console.log("get", req.flash("adminupdate"));
    res.render("admin/index", {
      pageTitle: "NodeShop",
      users: users,
      adminupdate: req.flash("adminupdate")
    });
  });
};

@bjamesy
Copy link

bjamesy commented Feb 14, 2019

TypeError: req.session.sessionFlash is not a function

im getting this error on the line - delete req.session.sessionFlash;

anything ?

@ayanesuarome
Copy link

Really helpful this post.
I'm using Vash view engine and it's easy to use as well.

@RealSlimMahdi
Copy link

Helpful post, manage to solve my issue (passing error as parameter when using rendering);
cheers!

@jmcombs
Copy link

jmcombs commented May 21, 2019

Thank you!!

@hughrun
Copy link

hughrun commented Jun 10, 2019

THANKYOU @brianmacarthur !!

@mvn-namnguyen-dn
Copy link

mvn-namnguyen-dn commented Jul 17, 2019

many thanks! Helpful for me

Copy link

ghost commented Sep 16, 2019

Hi @brianmacarthur. i have a question about this module and connect-flash module.
why i must use this modules, when i can use res.render() to send message?
res.render('path/to/view', { message: 'this is a warning message' })
please answer to this question.
read this stackoverflow Q&A for more details.

@brianmacarthur
Copy link
Author

@MJB-Khorasani You are right to question why sending messages to the browser must be accomplished in any particular way or require external dependencies. It doesn’t. There are a lot of different solutions to this problem. I just chose to highlight the most common one at the time (express-flash) and an alternative that drops that dependency. You can certainly take it further.

@MichaelAllenWarner
Copy link

@namsir

anyone got a bug where the first time you start the app, set the message in your post route, redirect to get route and it's not there. Do the post again, it shows 2 messages in the array. Post again, now it's working correctly. Is it a bug or something?

Yes, I've been dealing with this issue. It seems that there's some kind of race condition between express-session establishing the flash message and the browser's next request (triggered by the redirect).

I don't know whether this will still be useful to you, but I think I've finally solved this problem by manually saving the session immediately after setting the flash message, and triggering the redirect in the callback function of the save, like this (using your example):

req.flash("adminupdate", msg);
req.session.save(() => res.redirect("/admin"));

@curtiskeisler
Copy link

@MichaelAllenWarner, I concur. I found the same thing as well. I'd love to understand what's going on there. But, for me, req.session.save(...) and calling the redirect after the save in the callback works for me.

@atul-gautam-au6
Copy link

waoooo its very easy i am more confused.............for another things

@richlewis14
Copy link

richlewis14 commented Jun 19, 2020

req.session.save(() => res.redirect("/admin"));

@MichaelAllenWarner Thank you so much, this has just saved me from further hours of despair... Where in the documentation is this though? want to see what I missed when going through the docs

@MichaelAllenWarner
Copy link

req.session.save(() => res.redirect("/admin"));

@MichaelAllenWarner Thank you so much, this has just saved me from further hours of despair... Where in the documentation is this though? want to see what I missed when going through the docs

You're welcome. I remember this being quite frustrating.

Docs: https://github.com/expressjs/session#sessionsavecallback

@ZUHAIRAHAMED3114
Copy link

thanks for good explanation...

@Aearsears
Copy link

Thank you for your explanation!

@jelzo
Copy link

jelzo commented Oct 25, 2021

Disable browser cache if you have difficulties getting this to work! You could also add this in the middleware to acomplish this (results vary by browser):
res.set('Cache-Control', 'no-store')

@HosMercury
Copy link

very good

@Rogetz
Copy link

Rogetz commented Mar 13, 2023

Not really sure how the flash module is working here. I can get it to work yes but why can't I see the message disappearing after a refresh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment