Build a REST API with Node.js: Deploying to Heroku (Finale)

Subscribe to my newsletter and never miss my upcoming articles

Hello everyone! Welcome back to Let's Build a Node.js REST API Series - the finale. We are finally finishing this API. It's been a long series and we're finally here!

If you are new to this series, please check out the previous articles to follow along:

  1. Designing and Planning the API
  2. Routes and Controllers
  3. Integrating MongoDB Atlas
  4. Finalizing Controllers
  5. Upload an Image File

Today, we are going to prepare the API for deployment! To do that, first we need to add some security and compression.

Step 1: Security

We are using helmet to take care of our app security. Read the Must-Have Security Checklist for more details on helmet.

To install it, run:

npm install --save helmet

Then include the following in server.js:

// add this line below the other import statements
const helmet = require('helmet');

// add this line below const app = express();
app.use(helmet());

Another security feature we can add is route protection. This is because we don't want every user to have access to create new tea or to delete all tea in our API. That would be tragic!

For this API, I have implemented basic API header authorization to restrict access to certain routes but that is out of the scope of this series, as I want it to be as beginner-friendly as possible. A separate article on API authentication methods coming soon.

Step 2: Compression

We can compress HTTP requests to significantly reduce the time required for the client to get and load the page from the server. To do that, we can use compression.

Install it with:

npm install compression

Then add the following before the routes in server.js:

// add this line below the helmet import statement
const compression = require('compression');

// add this below app.use(helmet())
app.use(compression()); //Compress all routes

Step 3: Preparation for heroku

For this API, I'm deploying it to heroku. It is a cloud-based platform to build, deliver and monitor web apps such as this API. But there are plenty of options like:

  • AWS
  • DigitalOcean
  • Google Cloud
  • Firebase
  • Microsoft Azure
  • Many more...

1. Github

First, ensure you have your API in a Github repo. This is because heroku is integrated with git so it makes it easier for future changes.

2. package.json

Check your node version by running:

node --version

The console will output your Node version. Copy it, and include it in the "engines" key to add to your package.json:

  "engines": {
    "node": "12.14.1"
  },

And make sure your package.json has the following configuration for the "main" and "scripts" keys.

"main": "server.js",
"scripts": {
    "start": "node server.js",
    "test": "echo \"Error: no test specified\" && exit 1"  //optional
  },

3. Procfile and index.html

Create a file name 'Procfile' in the root directory and add

web:node server.js

This is to instruct heroku to run the command 'node server.js' as soon as it starts the app.

Optionally, create a index.html so the API would at least have a face when it first loads. I'm making a simple one with a heading and paragraph element.

<h1>Welcome to T-API</h1>
<p>The Tea API for all Tea Lovers~</p>

Remember to add its route in server.js so that index.html will be static, which allows the API to access it when the server starts.

// add this below app.use("/", routes) to make index.html a static file
app.route('/')
  .get(function (req, res) {
    res.sendFile(process.cwd() + '/index.html');
});

4. MongoDB

We are almost there! Finally, we add 2 more options into our mongoose.connect() method in our server.js file:

server: { 
   socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } 
}, 
replset: {
   socketOptions: { keepAlive: 300000, connectTimeoutMS : 30000 } 
}

This prevents heroku from returning a timeout error 503, just in case. Here's the final version of the mongoose.connect() method in our server.js file:

mongoose.connect(
  process.env.MONGODB_URI,
  {
    useFindAndModify: false,
    useUnifiedTopology: true,
    useNewUrlParser: true,
    useCreateIndex: true,
    server: { socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } },
    replset: { socketOptions: { keepAlive: 300000, connectTimeoutMS: 30000 } },
  },
  function (err) {
    if (err) return console.log("Error: ", err);
    console.log(
      "MongoDB Connection -- Ready state is:",
      mongoose.connection.readyState
    );
  }
);

Great! We have prepared what we need to deploy our app to heroku.

Step 4: Heroku

Create an account for free at heroku.com.

Then download the heroku CLI here and follow their instructions on that page to install it.

Once you have installed the CLI, you can now use heroku commands on your command prompt to deploy the API. Head to the project's root directory and run:

heroku create <app-name>

Then, let's make the final push:

git push heroku

Great! We have deployed the API! But because environment variables don't get deployed, we need to configure our process.env.MONGODB_URI first before starting the app.

Configure by running the command in your command prompt:

heroku config:set MONGODB_URI="<your url here>"

All done!

Finally, run the following to make sure an instance of the app always runs:

heroku ps:scale web=1

Let's visit the site with:

heroku open

The index.html will load as the entry page like shown below. It's just an empty white page with words for now. Make sure to make the file static so that it is accessible by the server to render. In server.js:

//Index page at default entry route
app.route("/").get(function (req, res) {
  res.sendFile(process.cwd() + "/index.html");
});

page.PNG

Right now, we don't have a user-friendly interface to test our API on the browser. But we can simply add our routes in the URL manually. As seen in the image above, my API is deployed at https://tea-api-vic-lo.herokuapp.com/. If I enter https://tea-api-vic-lo.herokuapp.com/tea, it should GET our '/tea' route and returned all our tea objects like so:

ugly.PNG

Of course, I went ahead to populate (POST) some tea objects first or the URL will return an empty object. As seen in the image above, the URL returns the tea object I created earlier correctly and therefore, the API works! Yay!

Congratulations!

We have finally finished building a working REST API with Node.js, MongoDB and Express! I hope this series has been very helpful for you in understanding routes, controllers, databases and how APIs work. If you visit the site where my T-API is deployed, you can see that I have made a fully responsive user interface for the API. I'll write and publish an article on how to create a front-end of an API soon so stay tuned.

Thank you for reading and following this series. I am very grateful to have received your generous words and feedback. We shall end off this series on a good note. Any comments or questions, as usual, please feel free to share them below. I hope you love tea and APIs now. Cheers!

👉 Here is where you can find my T-API: tea-api-vic-lo.herokuapp.com

👉 Refer to the Github repo if you need to see the source code: github.com/victoria-lo/TAPI

Further Reading

Bolaji Ayodeji's photo

Yayyy, it was an amazing series.

Thanks for sharing this and your dedication 🤩

Victoria Lo's photo

Thanks! Always glad to find you enjoying it :)

Fernando Torres's photo

Great tutorial! I've able to finish it!

I have a comment. The next line

heroku ps:scale web=1

must be after the "git push heroku" instruction on this post.

Thanks!

Victoria Lo's photo

Yayy congrats on finishing it! Thanks for the corrections too :)

Subha Chanda's photo

A whole series is a great resource to start learning Node. And one thing I must say, I loved the homepage of the API. 💝

Victoria Lo's photo

Haha thanks! I love the homepage too :)

Tapas Adhikary's photo

Your series is awesome! But, more than that, the dedication you showed to pull it.. So very awesome.. If not wrong, the entire series took just a week. Great going...

Victoria Lo's photo

Thanks so much for your constant support! Yes, I published 7 days in a row for this series so I'm gonna take a good long rest this weekend :)

Richard Harris's photo

Thanks Vic for the amazing dedication to write this series! Really learned a lot.

Victoria Lo's photo

Thanks Richard, I'm glad this series has been helpful for you :)