Properly deploy ReactJS with ExpressJS

Yes, this is just another article about deploying a ReactJS app.

I had to write this article.
I’ve started to develop with ReactJS four months ago and I’ve found out that the most frustrating part of the entire process is deploying the final product.

During the last two months I’ve experimented with Babel, Webpack and other amazing tools, but I’ve found out that `create-react-app` is the best way to get started with this amazing library. So that’s my starting point.

Why do I need ExpressJS?

I have this simple ReactJS application built on top of create-react-app and I just want to deploy it. 
Running npm run build I get a folder with a simple HTML file and its static dependencies. So now I have multiple choices:

  1. Deploy with Apache. I may want to deploy with Apache because I’m on a shared hosting and It can serve static HTML files. I could also have multiple VirtualHosts on my VPS, in this case Apache could help me a lot.
  2. Deploy with NGINX. NGINX would help me a lot managing SSL certs and multiple NodeJS instances, but I think that it can be a little complicated if my target is just to deploy a static HTML file.
  3. Deploy with ExpressJS. Here we are! I have a simple application and I need to access to some NodeJS APIs. I also want to take all the client side rendering advantages, so ExpressJS + ReactJS could be a great solution!

ExpressJS webserver

So this is what we’re going to do:

  • Get a new Ubuntu 16.04 LTS VPS
  • Create a new ExpressJS app
  • Setup SSL certs
  • Setup HTTP2 support

Please remember that in order to run an app under ports below 1024, you need root access to your VPS.

Let’s create a new ExpressJS app:

$ npm install express --save

Then we can create a new file:

$ touch server.js
$ nano server.js

This will be our webserver. Let’s write down some code:

var express = require('express');
var app = express();
const port = 3000;
app.get('/', function (req, res) {
res.send('I\'m running!');
});

app.listen(port, function () {
console.log(`ExpressJS is running on port ${port}!`);
});

Let’s pretend that our IP is 10.0.0.123, we can see our ExpressJS webserver in action by typing:

$ node server.js

Visiting http://10.0.0.123:3000, we’ll get:

I'm running!

Allright! Now we want to send a static HTML file every time that someone hits any of our routes.

That’s what we’re going to do:

const path = require('path');
const http = require('http');
const express = require('express');
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/build/index.html'));
});
const httpServer = http.createServer(app);
httpServer.listen(80);
console.log('Server started!');

This code will solve a lot of problems. When a user hits any of your routes (please note the wildcard route as `app.get()` first argument), we’ll send back a static file. In my case, React Router will do the rest with its amazing virtual routes.

Let’s check our folder structure:

$ ls
$ package.json server.js npm_modules

We need to create a new folder called “build” and guess what? We’ll update it with our ReactJs static build.

$ mkdir build
$ cd build
$ git clone https://yourrepository.com/react-build .

I think that Git is the easiest way to control your application deployment flow, but you can obviously update your build folder via SFTP or FTP.

Adding HTTPS / HTTP2 support

We are in 2017 and we need to add at least HTTPS support.
We’ll also add HTTP2 support by adding the library SPDY.

Let’s start by creating a new directory where we’ll store our certs:

$ mkdir certs
$ cd certs
$ touch server.key
$ touch server.crt

Now edit these new files with your SSL .crt and .key files.

nano server.key server.crt

Allright! Now let’s require SPDY library:

npm install express-spdy --save

We’re now ready to edit our server.js file:

const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const spdy = require('spdy')
const express = require('express');
const privateKey  = fs.readFileSync('certs/server.key', 'utf8');
const certificate = fs.readFileSync('certs/server.crt', 'utf8');
const credentials = {key: privateKey, cert: certificate};
const app = express();
app.use(express.static(path.join(__dirname, 'build')));
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/build/index.html'));
});
const httpServer = http.createServer(app);
const httpsServer = spdy.createServer(credentials, app);
httpServer.listen(80);
httpsServer.listen(443);
console.log('Server started over HTTP2 protocol.');

Yeah! We have correctly deployed our ReactJS application using ExpressJS, HTTPS and HTTP2 protocols!