The problem

As we were gearing up to migrate Culture Island from our development environments to a semi-production environment on Heroku, we needed the ability to render one of two index HTML files, depending on server side configuration. The first, index.html, would use our local, unconcatendated, unminified JS and CSS files. The second, index-dist.html, would use concatendated and minified JS and CSS. This sounded simple enough. I just had to define an Express route handler for '/', similar to the one below:

app.route('/')
  .get(function(req, res){
      if(config.useConcatendatedFiles){
          return res.sendFile(__dirname + '/public/index-dist.html');
      }
    return res.sendFile(__dirname + '/public/index.html');
  });

However, when I tried the above approach, the route handler was never being invoked. I threw some console.logs in there and set some server-side breakpoints, which confirmed that this code was never reached.

The cause

Ths issue was the way express was configured to handle requests for static files, as defined in the line below:

app.use(express.static(config.root + '/public'));

What this basically means is that any requests to any resource located in the {project root}/public folder will circumvent any route handlers, and simply respond with the requested resource as-is. Since requests to '/' retrieve public/index.html, my index route handler was being ignored.

The solutions(s)

I figured out two possible solutions:

  • Change the static file configuration to something like

    app.use(express.static(config.root + '/public/static'));
    

    then move all files except for the index HTML files into public/static. I would then have to update out Grunt tasks, such as jshint, jsmin, cssmin, etc, to read from and output to the new static folder instead of the base public folder. This seemed like an entirely too drastic change to the project structure, with a higher probability of introducing unintended side-effects. So I opted for second approach.

  • Convert index.html into a server-side view. This was relatively painless. This approach also has the added benefit of taking advantage of server-side caching of views, if configured to do so. The steps were to:

    • Create folder, server/views, and move index.html there.
    • Update the Express configurations:

      app.engine('html', consolidate[config.templateEngine]);
      app.set('view engine', 'html');
      app.set('views', config.root + '/server/views');
      
    • Update the index route handler as follows:

         app.route('/')
        .get(function(req, res){
            if(config.useConcatendatedFiles){
                return res.render('index-dist');
            }
            res.render('index');
        });