Deploy a web application using the Node.js, Express, MongoDB, React stack to Heroku.
By Steve Carey - 7/9/2019
Starting code: Github
This tutorial assumes you have a MERN application built. If you don't, you can build one with my two part tutorial: Build an API with Node.js, Express and MongoDB and Integrate React into a Node application.
Heroku requires you to use a cloud database platform for MongoDB. MongoDB's Atlas cloud service requires a user name and password as part of the URL. To keep these private you should use environmental variables so that they are not explicitly in your application's code. Assuming you are using Atlas for both development and production, you should add the dotenv package for your development environment if you expect to expose the file in a place like a public github repository. From the project's root directory:
npm install dotenv
In Node, environment variables can be accessed on the process.env object. The dotenv middleware looks for a file called .env and loads it's contents into the process.env variable for use in your code.
Create a .env file in the project's root directory:
touch .env
Add environmental variables on new lines in the form of NAME=VALUE. By convention, use names in all upper case with words separated by underscores like MONGODB_URI. Add your MongoDB Atlas database link since it contains your user name and password. Something like this:
# .env
MONGODB_URI=mongodb+srv://user:password@cluster-number.mongodb.net/test?retryWrites=true&w=majority
We are only using the .env file in our development environment. When you deploy your app on Heroku you need to add this as an environmental variable on Heroku at that time.
Update the server.js file with changes related to Heroku highlighted and explained below.
// server.js const express = require('express'); const mongoose = require('mongoose'); const router = require('./routes/index'); const path = require('path'); #1 const PORT = process.env.PORT || 3001; #2 require('dotenv').config(); #3 const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use('/api', router); mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true }); #4 mongoose.connection.once('open', () => { console.log('Connected to the Database.'); }); mongoose.connection.on('error', err => { console.log('Mongoose Connection Error : ' + err); }); if (process.env.NODE_ENV === 'production') { #5 app.use(express.static('client/build')); app.get('*', (req, res) => { res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html')); }); } app.listen(PORT, () => { console.log(`Server listening on port ${PORT}.`); });
const LOCAL_DB = "mongodb://127.0.0.1:27017/my_local_db";
mongoose.connect(process.env.MONGODB_URI || LOCAL_DB, { useNewUrlParser: true });
The React app in development is not optimized. We need to run the npm build (or yarn build) command for React to minify all your React code and put it into a folder called build. But actually we'll instruct Heroku to do that for you when you deploy. Add a script to the package.json file in the project root directory with property heroku-postbuild (bolded below). Heroku will run that script after it builds your app. The script cds into the client directory, runs npm install to install the React packages, and npm run build to build the optimized version in the build folder. Your finished package.json file should look something like the below.
// package.json
{
"name": "mern-app-tutorial",
"version": "1.0.0",
"description": "Web app built with Node.js, Express, MongoDB, and React",
"main": "server.js",
"dependencies": {
"dotenv": "^8.0.0",
"express": "^4.17.1",
"mongoose": "^5.6.3"
},
"devDependencies": {
"concurrently": "^4.1.1"
},
"scripts": {
"start": "node server.js",
"dev": "concurrently \"nodemon server.js\" \"cd client && npm run start\"",
"heroku-postbuild": "cd client && npm install && npm run build"
},
"keywords": [],
"author": "",
"license": "MIT"
}
Also note the "start" script. We used that in development to start the Express server with npm start. But we ignored it with our npm run dev script, using nodemon instead. But for production this script will come in handy since we want Heroku to run our server file with node once it's built.
Once Heroku is done building the app it will look for a file called Procfile for instructions on how to start the app. So we could create a Procfile and add one line web: npm run start
. If Heroku doesn't find a Procfile it will run the command npm start
. So that's why we need the start script to run node server.js
.
We will use git to push our app to Heroku.
When we generated our React app with create-react-app, it generated a git repository and created a gitignore file in the client folder. We want one git repository in our root directory for both our API and our React client. So if you haven't already, delete the .git repository in the client folder. The gitignore file is fine where it is. Git will honor multiple gitignore files so we'll just add another one at the root directory.
Now in our project's root directory initate a new git repository and create a .gitignore file.
git init
touch .gitignore
Add the .env file and the node_modules folders to our new gitignore file.
// .gitignore
node_modules
.env
This tutorial assumes you are already familiar with Heroku. If not, you need to first create an account on their website: heroku.com
Then download and install the Heroku Command Line Interface on your computer devcenter.heroku.com/articles/heroku-cli.
From the CLI, create a new Heroku App:
heroku create appname
If your appname is already taken you'll have to pick another one. Or leave off the appname and let Heroku generate a super awesome one for you. If you have a real domain name you can configure Heroku to use that devcenter.heroku.com/articles/custom-domains
This will automatically set heroku as your git remote repository. Run the below command to confirm. It will list the project's remote git repositories (if any) and their urls.
git remote -v
Set the MongoDB uri as an environmental variable (no quotes):
heroku config:set MONGODB_URI=Your db cloud link goes here
As of this writing there is no Heroku Add-On for the the MongoDB Atlas cloud service.
Create your initial git commit:
git add -A
git commit -m "Initial commit"
Then push the code out to heroku:
git push heroku master
It will take a few minutes to load up. When it's finished launch the app. The below command will open the app in your browser:
heroku open
If everything went well you have a working MERN application up and running on Heroku!