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:
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.
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.
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.
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:
- git init
- git add .
- 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.
menghout says
hello
Brian says
I keep receiving “/”.require is not a function
Jose Luis Muñoz says
Thank you, this was very helpful. I have some suggestions though.
I think you forget to add ho its called each file before adding the code for each one.
Also you can provide an project structure overview at the end of the article, to review and compare it.
Thanks a lot again!
bob says
Hi do you have a github repo for the code in this article?
Renatus Bernardo says
Thanks , big up well done and it was very helpfully to me .
RapidAPI Staff says
No problem, happy to help!
fake says
body returns empty object
Ruben says
Great tutorial!
I followed along this article and I found some obstacles such as express middleware. So once the code worked with some trivial modification, I created a repository at https://github.com/rushuifang/create-your-own-api. In case anyone needs it.
m. m. says
Thank you! This is coming in handy for me.
Joebest says
That was an awesome tutorial, I would be glad you create a tutorial on creating an API with a database instead of json files.
thanks regards
Gene says
this was an awful tutorial, needed to use app.use(‘/’, require(path.join(__dirname,’routes’,’stats.js’))); tutorial did not explicitly say to update that from just using routes