How to Build a RESTful API in Node.js (with Express.js)

If you’re in the tech industry, you probably already know you need to provide an easy way to discover and consume an API (Application Programming Interface).

Ways to provide public data are quickly evolving, and you need to offer data in a simple and powerful API.

Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine and is very useful in creating quick and scalable APIs.

So let’s write some Javascript and build a RESTful API in Node.js.

Requirements

To build a RESTful API, we will use:

  • Node.js
  • Express.js – Fast, unopinionated, minimalist web framework for Node.js

Installation

Let’s get started by installing all the necessary tools in order to run our Node.js API. You might have some questions about these tools, which we will answer in the details below.

  • Install nvm – Node Version Manager
  • Install npm – Node Package Manager
  • Install Node.js

Javascript is an ever evolving language with many versions of a library or framework. It can be quite difficult to always update packages and versions. Here is where nvm comes into play.

We can install nvm, which will help us install the latest version of npm and node.

Follow the instructions located here:

https://github.com/nvm-sh/nvm

Be sure to restart your command line terminal. Now you should be able to run npm and node:

npm --version

node --version

Implementation

Let’s create a repository of Sports Statistics using Football APIs.

With this API, any Node.js developer can quickly get stats about players stats.

Developers can also create a way to update stats on the fly.

Let’s start by initializing our project:

npm init

npm is the node package manager. It will describe what packages we need to run the server, how to run the server from the cli, and store what version of our code we are developing.

Running the above init command will ask you some questions, such as, what you want to name the project, licenses, and version.

Let’s just press “Enter” on every question to accept the defaults.

npm init rapidapi

This will create a file called package.json, which will include all of the project’s packages we need to run our API.

Installing Express

Express is a simple router for Node.js. It allows a developer to create simple endpoints quickly. There are many routers for Node.js, but Express is the most popular, with great documentation, and many examples.

Let’s install Express with npm:

npm install express body-parser morgan

We also need to install body-parser and morgan so that Express can accept JSON and provide logging.

We now have everything needed to start building our example API.

Let’s write some Javascript that will initialize and define our endpoints:

const express = require('express');
const bodyParser = require('body-parser');
const logger = require('morgan');
const path = require('path');
const app = express();

const PORT = process.env.PORT || 3000;
const NODE_ENV = process.env.NODE_ENV || 'development';

app.set('port', PORT);
app.set('env', NODE_ENV);

app.use(logger('tiny'));
app.use(bodyParser.json());

app.use('/', require(path.join(__dirname, 'routes')));

app.use((req, res, next) => {
  const err = new Error(`${req.method} ${req.url} Not Found`);
  err.status = 404;
  next(err);
});

app.use((err, req, res, next) => {
  console.error(err);
  res.status(err.status || 500);
  res.json({
    error: {
      message: err.message,
    },
  });
});

app.listen(PORT, () => {
  console.log(
    `Express Server started on Port ${app.get(
      'port'
    )} | Environment : ${app.get('env')}`
  );
});

This will create our Express server, listen on port 3000 by default, and run in development mode with the environment variable NODE_ENV.

Planning the API

It is best practice, when creating an API, to version your endpoints, that way you can maintain compatibility with older services, while continuing to improve your API.

https://www.ourapi.com is a placeholder that we will use. The production URL will be whatever domain you set up.

We will plan for our URL endpoint to look like this:

  • GET https://www.ourapi.com/api/v1/stats/101 – This will get the stats for player 101
  • POST https://www.ourapi.com/api/v1/stats – This will create the stats a player
  • PUT https://www.ourapi.com/api/v1/stats/101 – This will update the stats for player 101
  • DELETE https://www.ourapi.com/api/v1/stats/101 – This will delete the stats for player 101

We need to create a router, and define our endpoints, that will be used to read and create football player statistics.

Each record will contain:

  • an id of the Football player
  • wins
  • losses
  • points scored

Since this is a training example, we will store all entries in a JSON file. In a real-world application, we would probably use a database instead. 

  • Create a folder called routes
  • Create a file called stats.json
  • Create a file called stats.js

Creating the stats data structure

Let’s start by adding some default data in the stats.json file:

[
  {
    "id": 101,
    "wins": 10,
    "losses": 3,
    "points_scored": 7
  },
  {
    "id": 231,
    "wins": 1,
    "losses": 10,
    "points_scored": 2
  },
  {
    "id": 145,
    "wins": 5,
    "losses": 1,
    "points_scored": 14
  },
  {
    "id": 45,
    "wins": 6,
    "losses": 2,
    "points_scored": 2
  },
  {
    "id": 27,
    "wins": 14,
    "losses": 26,
    "points_scored": 28
  }
]

Now let’s begin with the GET method.

This method will return a players stats by providing an id in the url.

First we need to create an Express router. This example will just create a basic router. In the next example we will start creating the endpoints needed in our API.

const express = require('express');
const router = express.Router();

module.exports = router;

Express provides a way to create a router. On this router object you can create API endpoints.

Now let’s actually create a useful router with our first GET method:

const express = require('express');
const fs = require('fs');
const path = require('path');

const router = express.Router();

const getStats = async (req, res, next) => {
  try {
    const data = fs.readFileSync(path.join(__dirname, './stats.json'));
    const stats = JSON.parse(data);
    const playerStats = stats.find(player => player.id === Number(req.params.id));
    if (!playerStats) {
      const err = new Error('Player stats not found');
      err.status = 404;
      throw err;
    }
    res.json(playerStats);
  } catch (e) {
    next(e);
  }
};

router
  .route('/api/v1/stats/:id')
  .get(getStats);

module.exports = router;

It will read in the stats JSON file and locate the player. If it successfully found the player, the code will call res.json(playerStats) and return a valid JSON object.

If the player cannot be found, the method returns the error message and status code 404 Player stats not found.

Next, let’s create a POST method to create a new player.

Let’s add a new POST route to our previous router:

const createStats = async (req, res, next) => {
  try {
    const data = fs.readFileSync(statsFilePath);
    const stats = JSON.parse(data);
    const newStats = {
      id: req.body.id,
      wins: req.body.wins,
      losses: req.body.losses,
      points_scored: req.body.points_scored,
    };
    stats.push(newStats);
    fs.writeFileSync(statsFilePath, JSON.stringify(stats));
    res.status(201).json(newStats);
  } catch (e) {
    next(e);
  }
};

router
  .route('/api/v1/stats')
  .post(createStats);

This method will accept an id, wins, losses, and points scored from the request body.

Things to keep in mind when parsing JSON from a file is that it has to be valid JSON. When calling the JSON.parse function, you must wrap the code in a try / catch block so it won’t throw an error.

Then when saving the data back into the file, we need to call JSON.stringify, this will parse the JSON and return it as a valid string.

It will read in the data from the JSON file and append the new player stats, and will return with a status code of 201.

Now for updating a player’s stats with a PUT method

const updateStats = async (req, res, next) => {
  try {
    const data = fs.readFileSync(statsFilePath);
    const stats = JSON.parse(data);
    const playerStats = stats.find(player => player.id === Number(req.params.id));
    if (!playerStats) {
      const err = new Error('Player stats not found');
      err.status = 404;
      throw err;
    }
    const newStatsData = {
      id: req.body.id,
      wins: req.body.wins,
      losses: req.body.losses,
      points_scored: req.body.points_scored,
    };
    const newStats = stats.map(player => {
      if (player.id === Number(req.params.id)) {
        return newStatsData;
      } else {
        return player;
      }
    });
    fs.writeFileSync(statsFilePath, JSON.stringify(newStats));
    res.status(200).json(newStatsData);
  } catch (e) {
    next(e);
  }
};

Don’t forget to also add this method to the router:

router
  .route('/api/v1/stats/:id')
  .get(getStats)
  .put(updateStats);

This method will accept the players id in the url, and a request body with id, wins, losses, and points scored. Much like the POST request it will read in the stats data, find the correct player and update their stats.

If the update is successful it will return a 200 status code.

If the record cannot be found, the method returns the error message and appropriate status 404 Player stats not found.

Finally let’s create a DELETE method to remove the player’s stats

const deleteStats = async (req, res, next) => {
  try {
    const data = fs.readFileSync(statsFilePath);
    const stats = JSON.parse(data);
    const playerStats = stats.find(player => player.id === Number(req.params.id));
    if (!playerStats) {
      const err = new Error('Player stats not found');
      err.status = 404;
      throw err;
    }
    const newStats = stats.map(player => {
      if (player.id === Number(req.params.id)) {
        return null;
      } else {
        return player;
      }
    })
    .filter(player => player !== null);
    fs.writeFileSync(statsFilePath, JSON.stringify(newStats));
    res.status(200).end();
  } catch (e) {
    next(e);
  }
};

This will iterate through the stats data and remove the player.

If the record cannot be found, the method returns the error message and status code 404 Player stats not found.

Don’t forget to add this method to the router:

router
  .route('/api/v1/stats/:id')
  .get(getStats)
  .put(updateStats)
  .delete(deleteStats);

Our REST API is now ready to use!

Let’s add a way to start the API in the package.json file:

"scripts": {
  "start": "node index.js"
},

Now run the command:

npm start

This will start the Express server locally and make it ready to consume RESTful API endpoints.

If everything starts up you should see the following message in the console:

Express Server started on Port 3000 | Environment : development

Testing the API

If you have followed the above instructions you now have a running API.

We can test our API  using the PostMan REST client or by publishing it on RapidAPI.

Postman Test API RapidAPI

Deploying to Heroku

Heroku is a service that will let us deploy our API to the public so it anyone can discover and consume our data.

Click to to get learn more about getting started with Node.js.

Have an API you want to add to our API Marketplace?

#DIV_1 {
bottom: 0px;
box-sizing: border-box;
color: rgb(10, 10, 10);
cursor: pointer;
float: left;
height: 95px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(10, 10, 10);
text-size-adjust: 100%;
top: 0px;
width: 270px;
column-rule-color: rgb(10, 10, 10);
perspective-origin: 135px 47.5px;
transform-origin: 135px 47.5px;
caret-color: rgb(10, 10, 10);
background: rgb(0, 140, 220) none repeat scroll 0% 0% / auto padding-box border-box;
border: 1px solid rgb(227, 224, 224);
border-radius: 0 0 4px 4px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 0px 0px 15px;
outline: rgb(10, 10, 10) none 0px;
}/*#DIV_1*/

#A_2 {
box-sizing: border-box;
text-size-adjust: 100%;
perspective-origin: 0px 0px;
transform-origin: 0px 0px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
}/*#A_2*/

#DIV_3 {
bottom: 0px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
float: left;
height: 63px;
left: 0px;
position: relative;
right: 0px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 0px;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 31.5px;
transform-origin: 35px 31.5px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
border-radius: 5px 5px 5px 5px;
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 15px 5px 15px 15px;
outline: rgb(0, 0, 238) none 0px;
}/*#DIV_3*/

#IMG_4 {
bottom: -35.0781px;
box-sizing: border-box;
color: rgb(0, 0, 238);
cursor: pointer;
display: block;
height: 66.5781px;
left: 35px;
max-width: 100%;
position: absolute;
right: -35px;
text-decoration: none solid rgb(0, 0, 238);
text-size-adjust: 100%;
top: 31.5px;
vertical-align: middle;
width: 70px;
column-rule-color: rgb(0, 0, 238);
perspective-origin: 35px 33.2813px;
transform: matrix(1, 0, 0, 1, -35, -33.5);
transform-origin: 35px 33.2813px;
caret-color: rgb(0, 0, 238);
border: 0px none rgb(0, 0, 238);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
outline: rgb(0, 0, 238) none 0px;
}/*#IMG_4*/

#DIV_5 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
float: left;
height: 46px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 168px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 84px 23px;
transform-origin: 84px 23px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 400 normal 16px / 24px Lato, sans-serif;
margin: 22px 0px 0px 5px;
outline: rgb(255, 255, 255) none 0px;
}/*#DIV_5*/

#SPAN_6 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
width: 150px;
height: 27px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 107.047px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 53.5156px 13.5px;
transform-origin: 53.5156px 13.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 18px / 27px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_6*/

#SPAN_7 {
box-sizing: border-box;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
float: left;
height: 19px;
text-decoration: none solid rgb(255, 255, 255);
text-size-adjust: 100%;
width: 110.188px;
column-rule-color: rgb(255, 255, 255);
perspective-origin: 55.0938px 9.5px;
transform-origin: 55.0938px 9.5px;
caret-color: rgb(255, 255, 255);
border: 0px none rgb(255, 255, 255);
font: normal normal 700 normal 13px / 19.5px Lato, sans-serif;
outline: rgb(255, 255, 255) none 0px;
}/*#SPAN_7*/

Steps on deploying to Heroku

Before we can deploy to Heroku we need to register for a Heroku account and install the command line tools.

1. Heroku CLI

First, let’s install Heroku on macOS, you can also install on Windows, and Linux, the commands should be similar:

brew install heroku/brew/heroku

Now let’s log in to the Heroku CLI and authenticate so we can deploy our service:

heroku login

2. Initializing Heroku

To create a Heroku deployment we create a Procfile. This will tell Heroku how to start our app.

So let’s begin by creating a file called Procfile and add the following:

web: node index.js

This will tell Heroku that we want to start a web service, and start the application by calling the index.js file.

Next, Heroku offers a git repository for us, we will initialize and commit our files:

  1. git init
  2. git add .
  3. git commit -m “Node.js API first commit”

3. Creating a Heroku application

We need to create our Heroku application by running the command, this will provision the application for us:

heroku create

Next let’s push to our Heroku master branch:

git push heroku master

This will push our code to the repository in Heroku, and begin the process of starting our API service.

To start the service we need to tell Heroku how many application processes we want to scale to, in our case, let’s start with one:

heroku ps:scale web=1

Heroku also has a convenient way of opening the application in the browser:

heroku open

Now our service should be up and running. Let’s navigate to our API endpoint and confirm that it is serving our data by going to:

https://your-heroku-app.herokuapp.com/api/v1/stats/101.

This will return the player stats with the id of 101, and now we can confirm that our API is now up and running.

With our API being served publicly in Heroku, we can now create RapidAPI endpoints that will enable our service to be discovered.

Click here to get started with RapidAPI.

Conclusion

In this article, we learned how to install npm and node, building the RESTful API in Node.js, and the process of publishing the API on the Heroku cloud platform.

While this was an example of building an API, things to consider are connecting to a production database and scaling the Node.js API.

Related Resources

The post How to Build a RESTful API in Node.js (with Express.js) appeared first on Last Call – RapidAPI Blog.

Source: RapidAPI