Adding server side rendering (SSR) with fastboot to your Ember app

Server-side rendering using Fastboot is a technique that some websites are using to optimize their performance. Better said: to give a better user experience. Better-better said: to show some content to the user, like spinner and static parts of the site, while still loading async assets and dynamic data.

This is important to avoid the blank screen of death, so the user doesn’t think your site is broken. The site still has to do client side rendering after it fetches the app.js and the data, but at least the user knows something is happening. With that said, one could say that this is not 100% server side rendering. I guess you can make it 100% server side rendering using shoebox, but I’m not 100% sure here. I’m still learning about this. If you want to read more details about this I recommend reading this post from Tom Dale, co-creator of Ember.js and creator of fastboot.

Fastboot can be used with Express.js, which is a Node.js framework for web applications. We are here to install fastboot and deploy the site for production. We are going to add fastboot to a site using nginx in a fresh environment (no node installed).

Installing nvm

I like to use nvm to install and manage my nodejs. It’s easy to upgrade and change the version according to my app needs. I copied the following commands/comments from the README.md in the nvm repository.

# install nvm
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.6/install.sh | bash
# load nvm in this session
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" 
# install latest stable version of node
nvm install node 
# To set a default Node version to be used in any new shell, use the alias 'default':
nvm alias default node

If you run node -v you should see the version of your current nodejs.

Installing fastboot to your app

If your app is not complex, that’s great. If your app uses jQuery and some DOM manipulation (didInsertElement, willInsertElement), you might have some extra work. As I said, I’m still learning how to work with fastboot and so far I haven’t had any troubles with that.

To install it just use the install command below and run ember serve.

ember install ember-cli-fastboot
ember serve

Hopefully you can see some messages in your console log saying fastboot is being run.

App is being served by FastBoot
2017-12-03T00:14:06.495Z 200 OK /

Creating our Express server

There’s a project called fastboot-app-server that helps to create an express server using fastboot and we are going to use it for our case.

Let’s create a folder for our application and install it

mkdir fastboot-app
cd fastboot-app
npm install fastboot-app-server --save

Let’s create our simple server with the app now! Create a file named server.js and add this:

const FastBootAppServer = require('fastboot-app-server');

let server = new FastBootAppServer({
  distPath: 'publicFolder',
  host: '0.0.0.0', // Optional - Sets the host the server listens on.
  port: 4000, // Optional - Sets the port the server listens on (defaults to the PORT env var or 3000).
});

server.start();

Save and run now with node server.js. You should see something like this (probably with more workers):

[2017-12-03T01:02:34.510Z][m24993] using distPath; path=myproject/public/
[2017-12-03T01:02:34.554Z][m24993] forked worker 25004
[2017-12-03T01:02:34.555Z][m24993] worker online
[2017-12-03T01:02:37.235Z][w25004] starting HTTP server
[2017-12-03T01:02:37.272Z][w25004] HTTP server started; url=http://0.0.0.0:4000

Great! We are almost done! Let’s add a layer of security now by adding helmet. Install helmet by running

npm install helmet --save

Then edit your server.js file to use this middleware

// server.js
const FastBootAppServer = require('fastboot-app-server');
const helmet = require('helmet');

let server = new FastBootAppServer({
  distPath: '../public/',
  host: '0.0.0.0',
  port: 3000,
  beforeMiddleware: function(app) { app.use(helmet()); }
});

server.start();

Adding PM2

PM2 is a process management very handy for… managing processes (thanks captain obvious)! It helps to visualize processes running, how many times the process has restarted, if the process is paused, and so on. Let’s install and run our express server

npm install pm2 -g
pm2 start server.js

If everything went well. PM2 will show your app running with some information such as its name, PID, etc. We need to make the process run when our machine starts in case we want to restart it. If you read all the messages after the start command, you saw that it also shows a command for the startup of the machine. Let’s run it

pm2 startup systemd

If you noticed, the command didn’t really add anything to your systemd, but rather generated a command that we should run.

sudo env PATH=$PATH:/home/myuser/.nvm/versions/node/v9.2.0/bin /home/myuser/.nvm/versions/node/v9.2.0/lib/node_modules/pm2/bin/pm2 startup systemd -u myuser --hp /home/myuser

Adding our server to NGINX

Last step! We need to tell NGINX now to serve our application instead of our old app.

location / {
    proxy_pass http://localhost:4000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}

Reload your NGINX process (sudo nginx -s reload) and you should be good!

Sources

https://tomdale.net/2015/02/youre-missing-the-point-of-server-side-rendered-javascript-apps/ https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04 http://pm2.keymetrics.io/


Leave a Reply

Your email address will not be published. Required fields are marked *