In this tutorial we’re going to setup a production ready web server from scratch on the Amazon EC2 (Elastic Compute Cloud) service, then deploy a custom MERN Stack application to it that supports user registration and authentication.
A MERN Stack application is made up of a front-end app built with React that connects to a back-end api built with Node.js + Express + MongoDB, hence the name MERN Stack (Mongo, Express, React, Node). Other variations of the stack include the MEAN Stack that has an Angular front-end, and the MEVN Stack that has a Vue.js front-end.
This tutorial will be focused on setting up the server on AWS EC2, then deploying and configuring the front-end and back-end pieces of the MERN stack app to work together. For more in-depth information about the React app or Node.js api used in this post, check out the following tutorials which cover them in detail:
Before doing anything we need a server that we can work on, follow these steps to spin up a new Ubuntu 18.04 server instance on AWS EC2.
Once the EC2 instance reaches a running state you can connect to it via SSH using the private key downloaded in the previous step.
chmod 400 <path-to-key-file>
e.g. chmod 400 ~/Downloads/my-aws-key.pem
, the key must not be publicly viewable for SSH to work.ssh -i <path-to-key-file> ubuntu@<domain name>
e.g. ssh -i ~/Downloads/my-aws-key.pem [email protected]
yes
to the prompt “Are you sure you want to continue connecting (yes/no)?” to add the url to your list of known hosts.NOTE: If you’re using Windows you can connect to your instance via SSH using the PuTTY SSH client, for instructions see Connect Using PuTTY in the AWS docs.
The below command executes a script to automatically setup and configure a production ready MERN Stack web server on Ubuntu that includes Node.js, MongoDB, PM2, NGINX and UFW.
For more details about how the script works see Setup Node.js + MongoDB Production Server on Ubuntu.
While connected to the new AWS EC2 instance in the terminal window, run the following command:
curl https://gist.githubusercontent.com/cornflourblue/f0abd30f47d96d6ff127fe8a9e5bbd9f/raw/e3047c9dc3ce8b796e7354c92d2c47ce61981d2f/setup-nodejs-mongodb-production-server-on-ubuntu-1804.sh | sudo bash
Follow these steps to setup the Node.js API on the server and configure NGINX to enable access to it.
/opt/back-end
directory with the command sudo git clone https://github.com/cornflourblue/node-mongo-registration-login-api /opt/back-end
cd /opt/back-end && sudo npm install
sudo pm2 start server.js
The API is now running on Node.js under the PM2 process manager and listening on port 4000. Only port 80 (HTTP) is publicly accessible on the server so we can’t hit the API yet, this will be possible after we’ve configured NGINX as a reverse proxy to pass through HTTP traffic to the api (more on this shortly).
Follow these steps to setup the React application on the server.
/opt/front-end
directory with the command sudo git clone https://github.com/cornflourblue/react-redux-registration-login-example.git /opt/front-end
cd /opt/front-end && sudo npm install
Run the command sudo nano /opt/front-end/src/index.jsx
to open the main react entry file in the nano text editor.
Delete the following lines from the file to remove the fake backend that the react app uses by default:
// setup fake backend
import { configureFakeBackend } from './_helpers';
configureFakeBackend();
Save the file by pressing ctrl
+ x
and selecting Yes
to save.
Run the command sudo nano /opt/front-end/webpack.config.js
to open the webpack config file in the nano text editor.
Change the apiUrl
config property to '/api'
like below so it points to the public path we configured in NGINX in the previous section:
// global app config object
config: JSON.stringify({
apiUrl: '/api'
})
Save the file by pressing ctrl
+ x
and selecting Yes
to save.
sudo npm run build
The React app is now built and ready to be served from the /opt/front-end/dist
directory, in the next step we’ll configure our NGINX web server to enable access to it.
Since our MERN Stack application is made up of two separate projects that both need to be accessed via the same port (HTTP on port 80), we’re going to use NGINX as our public facing web server to receive requests for both the front-end and back-end, and decide where to send each request based on its path. Requests beginning with the path /api/*
will be proxied through to the Node.js api running on port 4000, while other requests will serve the React front-end app and associated files (js/css/images).
Follow these steps to configure NGINX for the MERN stack app.
Delete the default NGINX site config file with the command sudo rm /etc/nginx/sites-available/default
Launch the nano text editor to create an new default site config file with sudo nano /etc/nginx/sites-available/default
Paste in the following config:
server {
listen 80 default_server;
server_name _;
# react app & front-end files
location / {
root /opt/front-end/dist;
try_files $uri /index.html;
}
# node api reverse proxy
location /api/ {
proxy_pass http://localhost:4000/;
}
}
Save the file by pressing ctrl
+ x
and selecting Yes
to save.
Restart NGINX with the command sudo systemctl restart nginx
server { ... }
defines a server block which contains the configuration for a virtual server within NGINX.
listen 80 default_server;
uses the listen directive to configure the virtual server to accept requests on port 80
and sets it as the default virtual server on this NGINX server.
server_name _;
uses the server_name directive to set the server name to an underscore (_
) to make this server block a catch-all block that matches any domain name that doesn’t match another more specific server block. Since this example has only one server block it will match all domain names.
location / { ... }
defines a location block which contains the configuration for requests that have a URI beginning with a forward slash (/
), unless the request URI matches another more specific location block.
root /opt/front-end/dist;
uses the root directive to set the root directory to the front end dist folder (/opt/front-end/dist
) for requests matching this location block.
try_files $uri /index.html;
uses the try_files directive to first check for the existence of a file matching the request URI ($uri
) and returning it if there is one. If no file matches the request URI then it defaults to returning /index.html
.
location /api/ { ... }
defines a location block which contains the configuration for requests that have a URI beginning with /api/
.
proxy_pass http://localhost:4000/;
uses the proxy_pass directive to proxy requests beginning with /api/
through to the Node.js API running at http://localhost:4000/
.
Enter the hostname of your AWS EC2 instance in a browser to access and test your new MERN stack application.
The hostname is the “Public DNS (IPv4)” property located on the instance description tab in the AWS Console.
☞ Using Python to Automate AWS Services | Lambda and EC2
☞ Uploading Videos Using Multer - Let's Build A Youtube Clone with ReactJS, NodeJS, MySQL
☞ Coding Livestream - Building A Social Media W/ ReactJS, NodeJS, MySQL
☞ React Passwordless Authentication Tutorial | React, NodeJS, Stytch API
☞ Socket.io + ReactJS Tutorial | Learn Socket.io For Beginners