Pedro Roque

Pedro Roque

Software Architect

Deployment of a React application in a Node.js webApp, hosted on Azure

February 14, 2020
devops
azure
pipelines
react

Recently, I had to create a pipeline for publishing a React application on Azure. Normally, I would publish the application in blob storage, and problem solved. However, in this application, we use React routing, and we want to offer the possibility for users to arrive at a page with a link like https://site.com/product/xyz.

The problem is that this link doesn't lead anywhere on the server. If we use blob storage hosting, the result would be a 404. Not good!

We need to have a server that gives us the possibility of, for any route, always returning the index.html file.

Since the entire React development process heavily uses Node.js, we decided that the best course of action was to deploy the application on a Node.js webApp.

To do this, we first needed to configure the express server. Fortunately, this is something that can be done with just a few lines of code.

The structure of our projects looks something like this:

1application
2└───build
3└───public
4└───src
5│   │   file011.jsx
6│   │   file012.jsx
7│   │
8│   └───subfolder1

When we build the application, the result is combined with the contents of the public folder, and everything ends up in the build. So, it seems obvious that our server has to be configured somehow in the public folder. And this is done with just two commands executed in the public folder:

1npm init
2npm instal express

This will create a new package.json file in the public folder (actually, there are two files, but who really cares about the package-lock.json?)

1{
2  "name": "reactApp",
3  "version": "1.0.0",
4  "description": "my react app",
5  "main": "index.js",
6  "scripts": {
7    "start": "node index.js"
8  },
9  "author": "",
10  "license": "ISC",
11  "dependencies": {
12    "express": "^4.17.1"
13  }
14}

Now we need our server. From the contents of package.json, we see a reference to something called index.js. Maybe it's a good idea to create an index.js!

1const express = require("express");
2const server = express();
3const options = {
4  index: "index.html"
5};
6
7server.use("/", express.static("/home/site/wwwroot", options));
8
9server.use((req, res) => res.sendFile(`${"/home/site/wwwroot"}/index.html`));
10
11server.listen(process.env.PORT);

The first line configures the server to serve static content. This will ensure that the files of our application are served by express.

The second instruction redirects all requests to index.html, the entry point of our application. When the page is rendered in the browser, the React router takes over and will render the requested page.

Now we just need to adapt the publication step in the Azure Pipeline to make everything work:

1- task: AzureRmWebAppDeployment@4
2    inputs:
3        ConnectionType: AzureRM
4        azureSubscription: '$(azureSubscription)'
5        appType: webAppLinux
6        WebAppName: '$(serverWebAppName)'
7        packageForLinux: '$(System.ArtifactsDirectory)/drop/$(Build.BuildId).zip'
8        StartupCommand: 'node index.js'
9        ScriptType: 'Inline Script'
10        InlineScript: 'npm install'
1ScriptType: 'Inline Script'
2InlineScript: 'npm install'

Ensure that npm install is executed after the deployment, and

1StartupCommand: 'node index.js'

Configure index.js as the server's entry point.