Introduction
Here in this guide, we’ll discuss integrating React frontend with Express backend and to deploy on an apache server.
React
ReactJS is one of the most developer-friendly frameworks since when it released in 2013. It’s versatile and easy to master and learning it will give you an advantage over any other front end frameworks ever been created. React provides a concept of virtual DOM which is a copy of HTML DOM API. As for most of the developers around, we all know that manipulating DOM is a little hard when we want to update certain parts of the DOM without actually rendering the entire DOM. That’s where react has shown it’s actual usage. React developers can make use of virtual DOM and build components which can be used across the application development.
Let’s take a look at google trends search reports of React vs Angular vs Vue.
Express
There was a time when JavaScript can be used only for frontend activities such as manipulating the DOM and for adding behaviour to the web pages. But things were changed much different when Ryan Dahl released the first version of NodeJS on 2009 which is a serverside run time environment for javascript. (Yeah! we all know that).
ExpressJS is a web application framework for NodeJS designed to develop web applications and APIs. Creating a server in JavaScript has never been so easy as express do. Let’s take a look at creating a very basic web server in Express.
const express = require('express') const app = express() app.get('/', function (req, res) { res.send('Hello World') }) app.listen(3000)
Express + React
In this tutorial we’ll create a very basic application in which we’ll use React as the frontend and Express as a backend server to handle HTTP requests. So let’s start building our application.
Directory structure
$ sudo mkdir exp_react
$ cd exp_react
~ exp_react$ yarn init -y
$ sudo mkdir backend
~exp_react$ create-react-app frontend
In the exp_react folder add the following inside the package.json and run yarn to install node packages.
{ "name": "exp_react", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "install": "(cd client && yarn) && (cd backend && yarn)", "start": "concurrently \"cd frontend && PORT=3000 yarn start\" \"cd backend && PORT=3001 yarn start\"", "build": "concurrently \"cd frontend && yarn build\" \"cd backend && yarn build\"", "start:prod": "cd backend && yarn start:prod", }, "dependencies": { "babel-cli": "^6.26.0", "concurrently": "^5.3.0" }, "engines": { "node": "12.14.1" } }
We’ll run react application on port 3000 and express application on port 3001 also we’ll be using concurrently to run both applications at the same time. Now we can set up our backend to handle the HTTP requests coming from our React application.
$ cd backend && sudo nano package.json { "name": "backend", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "start": "nodemon -r babel-register server.js", "build": "babel . --ignore node_modules,build --out-dir build", "start:prod": "node build/server.js" }, "dependencies": { "babel-cli": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-es2015": "^6.24.1", "babel-preset-stage-0": "^6.24.1", "body-parser": "^1.19.0", "concurrently": "^5.3.0", "express": "^4.17.1" }, "devDependencies": { "babel-register": "^6.26.0", "nodemon": "^2.0.4" } }
Once you are added the above commands in package.json run yarn to install the dependencies node modules. As you can see we’ll be using nodemon to re-render the application whenever any changes occur to the application which is pretty useful for development.
Also, we’ll create a .babelrc file. babelrc file is the local configuration for the codes in our project. Generally, you would put it in the root of your application directory. It will affect all files that Babel processes that are in the same directory or in sibling directories of the .babelrc.
{ "presets": ["es2015", "stage-0"], "plugins": ["transform-runtime"] }
Now we can create the server.js file which will be handling all the HTTP requests. Create the file and add following codes to it.
import bodyParser from 'body-parser'; import express from 'express'; import path from 'path'; const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended:true})); const router = express.Router(); router.get('/cities', (req,res,next) => { const cities = [ {name: 'New York City', population: 8175133}, {name: 'Los Angeles', population: 3792621}, {name: 'Chicago', population: 2695598} ] res.json(cities) }); const staticFiles = express.static(path.join(__dirname,'../../frontend/build')) app.use(router); app.use(staticFiles) app.set('port', (process.env.PORT || 3001)); app.listen(app.get('port'), () => { console.log(`Listening on ${app.get('port')}`) })
React frontend
Coming to the frontend part edit the package.json file with the following codes.
{ "name": "frontend", "version": "0.1.0", "private": true, "proxy" : "http://localhost:3001", "homepage": "http://localhost/expreact", "dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.3" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
As you can see we have added a proxy to http://localhost:3001 to the express application also mentioned the home page for our application. Open the App.js file and add the following codes to it.
import React, { Component } from 'react'; import './App.css'; class App extends Component { state = {cities: []} async componentDidMount() { const response = await fetch('/expreact/cities') const cities = await response.json() this.setState({cities: cities}) } render() { return ( <div> <ul> {this.state.cities.map( city => { return <li key={city.name}> <b>{city.name}</b>: {city.population}</li> })} </ul> </div> ); } } export default App;
We have made a fetch call the express backend and generated a list which will render the city name and city population inside an unordered list. Now go back to the main directory and run yarn start which will open up a development web server.
$ yarn start
The development server will listen to localhost:3001 and run the react application concurrently. Further, we can build our application and serve from the apache server. To do that open up a terminal and run.
$ npm run build or yarn build
This will generate a build folder inside the backend directory which we can use to serve the application on an apache server.
Deployment
Connect to your apache server and move the entire directory into the server. Once you are done with that we’ll use the pm2 node package to run the application on the background. To do that open up a terminal on the server and run
$ npm install -g pm2
$ sudo chmod 755 /backend/build/server.js (build file)
$ sudo pm2 start /backend/build/server.js (Adds to pm2 task)
$ sudo pm2 startup systemd
Finally, create a new virtual host on the apache server to redirect all traffic to this application.
$ sudo a2enmod proxy && sudo a2enmod proxy_http
$ sudo nano /etc/apache2/sites-available/expreact.conf
<VirtualHost *:80>
ServerName localhost(can be domain)
ProxyRequests Off
ProxyPreserveHost On
ProxyVia Full
<Proxy *>
Require all granted
</Proxy>
<Location /expreact>
ProxyPass http://127.0.0.1:3001
ProxyPassReverse http://127.0.0.1:3001
</Location>
<Directory "/project/server/build">
AllowOverride All
</Directory>
</VirtualHost>
Save the file and enable the new virtual host using the command.
$ sudo a2ensite expreact.conf
$ sudo systemctl reload apache2