Skip to content

Instantly share code, notes, and snippets.

@amandeepmittal
Created October 9, 2018 08:40
Show Gist options
  • Save amandeepmittal/bb06cb5b585c5b31ec375fe8d91f2560 to your computer and use it in GitHub Desktop.
Save amandeepmittal/bb06cb5b585c5b31ec375fe8d91f2560 to your computer and use it in GitHub Desktop.

Deploy a MERN Stack Application on Heroku

In this article, I will describe how to take an existing Web Application that is build using MongoDB, ExpressJS, Node.js and Reactjs (often called as MERN stack) on a deployment service like Heroku. If you have an existing application built using the same tech stack, you can definitely skip the process in which I show you to quickly build a web application and go straight on to the deployment part. For the rest of you, please continue to read.

MERN Stack

MongoDB, ExpressJS, Node.js and Reactjs are used together to build web applications. In this, Node.js and Express bind together to serve the backend, MongoDB provides a NoSQL database to store the data and frontend is built using React that a user interacts with. All four of these technologies are open source, cross-platform and JavaScript based. Since they are JavaScript based, one of the main reasons why they are often used together.

As JavaScript is used throughout to build a Fullstack application, developers do not need to learn and change the context of using different programming languages to build or work on different aspect of a web application.

To continue to follow this tutorial there are requirements that you will need to build the demo application and then deploy it on Heroku.

  • Node.js/npm installed
  • Heroku account

For MongoDB, we are going to use a cloud based service like mLab which provides database as a service. It has a free tier, and having an account there will be time saving.

Building a MERN Stack Web App

Building the Backend

I am going to take you through building a web application using MERN Stack. To start, please create an empty directory and type the following commands in the order they are specified.

https://gist.github.com/00cd17a2e874564cbf6a1817b9cd9cad

Create a server.js file inside the root of the directory. This file will server us the backend file for us.

https://gist.github.com/77c129ff88f04d5951b8e06fd7e308bc

Now, I made following changes in package.json for this program to work.

https://gist.github.com/33bc340ac3cd4b7936e3d86b41338370

To see if everything is working, run the command npm start server that we just defined in package.json as a script. If there are no errors, you will get the following result. Visit the following url: http://localhost:5000.

~[mern-1]

Please note that, onwards Express version 4.16.0 body parser middleware function is a built-in middleware and there is no need to import it as a separate dependency. Body parser middleware is required to handle incoming AJAX requests that come in the form of JSON payloads or urlencoded payloads.

Models with Mongoose

When I am not writing JavaScript, I am huge bibliophile. Thus, for this demonstration, I am going to build a web application that tends to take care of all the books that I want to read. If you are into books, you can think of it is as your own personal TBR manager.

I will start by creating a database model called Book inside the file models/Books.js. This will resemble a schema of what to expect from the user when adding information to our application.

https://gist.github.com/4ef1eda779c0fbf29bc66af46e17905f

I am using mongoose to define the schema above. Mongoose is an ODM (Object Document Mapper). It allows you to define objects with a strongly typed schema that is mapped as a MongoDB collection. This schema architecture allows us to provide a organized shape to the document inside the MongoDB collection.

In our bookSchema we are defining two fields: title which indicates the title of the book and author for the name of the author of the book. Both these fields are string type.

Defining Routes

Our application is going to need some routes that will help the client app to communicate with the server application and perform CRUD (Create, Read, Update, Delete) operations. I am defining all the business logic that works behind every route in a different file. Conventionally, named as controllers. Create a new file controllers/booksController.js.

https://gist.github.com/fd73f97b37d2c49c750b4ee33100ed2e

The business logic or you can say the controller logic behind the application is nothing but the methods that will work on a specific route. There are five functions in total. Each has its own use. I am requiring our Book model, previously created, as it provide functions for us to query CRUD operations to the database. A mongoose query can be executed in two ways, by providing a callback function or by using .then() function which also indicates that mongoose support promises. I am using promising approach above to avoid the nuisance caused by nested callbacks (and commonly known as callback hell).

Next step is to use these methods in our routes inside routes/ directory. Create a new file called books.js.

https://gist.github.com/5574335360d11d6705cdd52195c35b04

I have separated the concerned routes that matches a specific URL. For example, routes that are starting with :id routing parameter are defined above together in the file. Open index.js in the same directory and add the following.

https://gist.github.com/bbea52df9d6d571cabd317940f0a3954

I have added a prefix /api/books before our routes. This way, you can only access them as http://localhost:5000/api/books.

For this to work, I am going to import book routes in the server.js file after every other middleware defined and before we have bootstrapped the server.

https://gist.github.com/da7cc78e5a965cebe2ca70137e43d52c

Also remove the default route app.get('/')... that we previously created. We are soon going to serve the application's front end here.

Connecting with Database using mLab

I am going to use mlab to host the database of our application on the cloud. Once you create an account, you dashboard will look similar to mine. I already have few sandboxes running, so do not mind them.

~[mern-2]

To create a new one, click on the button Create New under MongoDB deployments. After that, you select the plan type Sandbox which provides the free tier up to 500MB.

~[mern-3]

After the MongoDB deployment is created, a database user is required by the mlab to have you connect to this database. To create one now, visit the 'Users' tab and click the 'Add database user' button.

~[mern-4]

Now copy the string provided by mlab such as: mongodb://<dbuser>:<dbpassword>@ds125453.mlab.com:25453/mern-example and add the username and password you just entered to create the new user. I am going to save these credentials as well as the string given by mlab to connect to the database inside a file called config/index.js.

https://gist.github.com/bf4ae7393a1d39b532f18c0b6e6b2671

You can replace the x's for dbuser and dbpassword. Now to define the connection with mlab string we are again going to use mongoose. Create a new file inside models/index.js.

https://gist.github.com/b78210f662fc84e4cb579e8cae749edd

We are importing the same database URI string that we just exported in config. I am going to require this file inside our server.js before any middleware is defined.

https://gist.github.com/1ec2534f38c35561e2b88ffb5f0de15f

Now run the server again and if you get the following message, that means your database is gracefully connected to the webs server.

~[mern-5]

Building the FrontEnd with React

To build the user interface of our application, I am going to create-react-app. Run the following command to generate a react application inside a directory called client.

https://gist.github.com/a35f3a59cedf4282fadb08bbb267a6e5

Once the scaffolding process is complete, run npm run start after traversing inside the client directory from your terminal, and see if everything works or not. If you get a screen like below that means everything is top-notch.

Install two dependencies from npm that we need to in order for the client to work.

https://gist.github.com/83eb7d9e4a4a1f6aba618915356c6fee

You are going to need axios to make AJAX requests to the server. react-router-dom is for switching between navigation routes.

You can download the whole source code or just the client from this Github repository. I am not going walk you through every component and reusable component I have built in this application. I am only going to take you through what needs to be done connect the React app to Node.js server, the build process and then deploying it.

The main frontend file, App.js looks like this:

https://gist.github.com/2faf71c5325ffc8d61f440b6b0064c18

Next, I have created an API.js inside the utils directory which we handle all the requests and fetching data, in simple terms AJAX requests between our client and the server.

https://gist.github.com/6b2c44251cee554b42f7d8aeeae1572c

We also have an pages and a separate componentsdirectory. The pages contain those files that our going to be display the content when we add a book and its author in our list using a form to submit the data to the backend. The form itself uses different reusable components which our built separately. The sole purpose of doing this is to follow best practices that are convenient to understand the source code and a common practice in the React community.

There are two pages Books and Details. Let us go through them.

https://gist.github.com/90f0bae61778dc8b799ffd163bafdc3b

We are defining a local state to manage data and pass it on to the API from the component. Methods like loadBooks are make AJAX requests through the API calls we defined inside utils/API.js. Next is the details page.

https://gist.github.com/d82466c8ae036fdf43706f04719d0607

It shows the books I have added in my list. To use it, first we are going to connect it with Node.js.

Connecting React and Node

There are two build steps we have to undergo through in making a connection between our client side and server side. First open the package.json file inside the `client directory and enter a proxy value that points to the same URL on which server is serving the API.

https://gist.github.com/8ca41ad9d39e88af685d31c7a3c8872a

Next step is to run the command yarn build inside the client directory such that it builds up the project. If you haven't run this command before in this project, you will notice a new directory suddenly appears.

[mern-8]

We also need to make two changes to our backend, to serve this build directory. The reason we are doing this is to deploy our fullstack application later on Heroku as one. Of course, you can two deployment servers where one is serving the REST API such as our backend and the other serves the client end, the build folder we just created.```

Open routes/index.js and add the following line.

https://gist.github.com/67617e47fd09ccab2a39234969c1480e

Next, open the server.js to in which we add another line using Express built-in middleware that serves static assets.

https://gist.github.com/dbbd0dedf052d80c7f99ffd0d3694fcd

Now you can open your terminal and run the following command.

https://gist.github.com/7faf87167e5395b090e76584627395c0

This will trigger our server at url http://localhost:5000. Visit it using a browser and see your MERN stack app in action like below. For brevity, I haven't much styled but go ahead and showcase your CSS skills.

[mern-7]

To verify that the data from our application is being added to the database, go to your mlab MongoDB deployment. You will notice a collection appearing with name of books. Open it and you can see the data you have just submitted through the form. Here is how mine looks like.

[mern-9]

I have added two records.

[mern-10]

[mern-11]

Since everything is running locally without any problem, we can move to the next part.

Deploying on Heroku

This is our final topic in this tutorial. Now, all you need is to have a free Heroku account and Heroku toolbelt to run the whole deployment process from your terminal.

The Heroku Command Line Interface (CLI) makes it easy to create and manage your Heroku apps directly from the terminal. It’s an essential part of using Heroku.

To download the Heroku CLI interface visit this link. Depending on your operating system, you can download the packager. You can also choose a simpler method that is to install the cli interface using npm: npm install -g heroku.

After you go through the download and installation process, you can verify that everything has installed correctly.

https://gist.github.com/f782a6bcbc66aa406a21d83a2e36a7a9

Modify package.json by adding the following script.

https://gist.github.com/e17ec7f5c9408441d591040e9635787a

Login to your Heroku account with credentials by running command heroku login like below.

[mern-12]

Next, create a Procfile in the root folder with following value.

https://gist.github.com/6ce4b786b04a65838969e3f992ff518f

Once you are logged in traverse to the project directory of your MERN stack application. Run the following command to create a heroku instance of your app. Make sure you are in the main directory and not in the client folder.

Before we go on to prepare our project for Heroku, I am going to use git to push our current project. This is the most common and safe way to use it with heroku cli interface. Run the following commands in the order described.

https://gist.github.com/536dc1282701904d2355c9ca60772143

Then run

https://gist.github.com/58458d7bc387674f7de1e50d225dfdce

When this command runs successfully, it gives you can app id like this. Remember this app id as we are going to use it set our existing mlab MongoDB URI.

[mern-13]

Next step is to connect the existing mlab deployment from our Heroku app.

https://gist.github.com/69c80f62898c7329cf0d108e71629289

You can also use the free tier of mlab provided by heroku using the following command in case you haven't deployed your database previously. This command must only be run in case you are not already using mlab for your mongodb deployment.

https://gist.github.com/ee6890399b191c1a1c39e38f7a60772a

You can verify that configuration has been set by running:

https://gist.github.com/ce8e9816e10610c9551e235309bfe8ec

Note that user and password in above commands are the your mlab credentials that have been discussed on how to setup them in previous sections of this article. Next step is to push to heroku.

https://gist.github.com/4a0977d669a80ac132a55c612c4d7b25

This points to Heroku remote instead of origin. This above command sends the source code to Heroku and from then Heroku readspackage.json only to install dependencies. That is the reason we defined start script rather using the than server one because Heroku or an deployment server has no use of development environment dependencies such as nodemon.

Finishing the building of your project may look similar to mine.

[mern-14]

You can then visit the URL given by Heroku like below. Do notice the already existing data that we deployed using local server in the previous section.

[mern-15]

Conclusion

There are many deployment techniques that you can try on Heroku. The technique described in this article is just on of them. I hope you have this article has helped you out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment