REST API

It is another average fall day in the Twin Cities of Minneapolis and St. Paul. It is sunny and cold. Two more days for the weekend; not that it matters because we are still in the COVID-19 pandemic. That said things appear to be getting better. Hopefully a vaccine will be available soon and we will all get into a new normal.

I am still reading the book Hacker’s Delight by Henry Warren. I have a couple more chapters that I would like to finish before putting it down and starting Get Programming with Node.js by Jonathan Wexler. As I mentioned a few months ago, I will be working on a storage project for my blog. I will start with the APIs and database interface. I decided to use MongoDB for the metadata and the file system for the data. The server part code will be implemented using Node.js in JavaScript. After we have the base functionality we will split the software to work on multiple servers. Once that is done we will move to place the modules in Docker containers and will manage them with Kubernetes. Once all is well, I will see if we can move it to the Azure cloud. After that we will create and integrate additional features.

Today I spent some time refreshing Node.js. I have used it on and off for a couple years, but I like to refresh before starting a project. That is why I have also purchased on Amazon the book Get Programming with Node.js which should be arriving later this week.

REST is an architectural style as well as an approach for communications purpose that is often used in various web services development. It is often regarded as the “language of the Internet”. It is a stateless client and server model.

The methods of the REST API are CRUD (Create Read Update Delete) and are used on resources.

HTTP methods:

C --->	Create	--->	POST
R --->	Read   	--->	GET
U --->	Update	--->	PUT
D --->	Delete	--->	DELETE

The CRUD operations match nicely with a set of REST requests.

In this post we will implement and experiment with a simple REST API. The API will deal with a set of books. In our example we will not use a database. We will just use a small array of data.

Microsoft Windows [Version 10.0.18363.1139]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\johnc>node --version
v12.18.1

C:\Users\johnc>cd workspace4

C:\Users\johnc\workspace4>dir
10/27/2020  02:50 PM    <DIR>          .
10/27/2020  02:50 PM    <DIR>          ..
10/15/2020  10:37 AM    <DIR>          BackspaceStringCompare
10/21/2020  10:46 AM    <DIR>          CoinChange
10/21/2020  04:36 PM    <DIR>          CoinChange2
10/27/2020  02:50 PM    <DIR>          DemoForRESTAPI
10/19/2020  11:23 AM    <DIR>          FibonacciDynamicComputing
10/22/2020  07:20 AM    <DIR>          LRUCache
10/26/2020  08:30 AM    <DIR>          PathWithMinimumEffort

C:\Users\johnc\workspace4>cd DemoForRESTAPI
C:\Users\johnc\workspace4\DemoForRESTAPI>

For starters we will check the version of Node.js installed in the Windows 10 machine. Will initially work on Windows and in the future will move to Linux. It seems that on most public clouds Linus servers are less expensive than Windows.

The code for our REST API will be located in the DemoForRESTAPI folder in a Windows computer. During the generation of this post I will move the code to GitHub. As usual you should be able to find link towards the end of this post.

C:\Users\johnc\workspace4\DemoForRESTAPI>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (demoforrestapi) demo
version: (1.0.0)
description: Hands-on for creation of REST API
entry point: (index.js) script.js
test command:
git repository:
keywords:
author: John Canessa
license: (ISC)
About to write to C:\Users\johnc\workspace4\DemoForRESTAPI\package.json:

{
  "name": "demo",
  "version": "1.0.0",
  "description": "Hands-on for creation of REST API",
  "main": "script.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "John Canessa",
  "license": "ISC"
}

Is this OK? (yes)

C:\Users\johnc\workspace4\DemoForRESTAPI>dir
10/27/2020  02:56 PM               246 package.json

To start a project we need to get to the folder of interest and run the npm init command. You will be prompted for some information. We will name the entry point for our project:  script.js, you can use a different name.

We will use Express.js. Express is a back end web application framework for Node.js, released as free and open-source software under the MIT License. It is designed for building web applications and APIs. It has been called the de facto standard server framework for Node.js.

C:\Users\johnc\workspace4\DemoForRESTAPI>npm i express
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN demo@1.0.0 No repository field.

+ express@4.17.1
added 50 packages from 37 contributors and audited 50 packages in 4.907s
found 0 vulnerabilities

Installing Express is quite simple. It just requires npm.

We will also need to install Joi (a powerful schema description language and data validator for JavaScript). You can find the Joi API here.

C:\Users\johnc\workspace4\DemoForRESTAPI>npm i joi
npm WARN demo@1.0.0 No repository field.

+ joi@17.3.0
added 6 packages and audited 56 packages in 1.175s
found 0 vulnerabilities

I installed the Joi software.

Another piece of software which I like to use is Nodemon. Nodemon is a utility that will monitor for any changes in your source and automatically restart your server. It is perfect for development. Just use nodemon instead of node to run your code, and now your process will automatically restart when your code changes. This happens after you manually save changes.

C:\Users\johnc\workspace4\DemoForRESTAPI>npm i -g nodemon
C:\Users\johnc\AppData\Roaming\npm\nodemon -> C:\Users\johnc\AppData\Roaming\npm\node_modules\nodemon\bin\nodemon.js

> nodemon@2.0.6 postinstall C:\Users\johnc\AppData\Roaming\npm\node_modules\nodemon
> node bin/postinstall || exit 0

Love nodemon? You can now support the project via the open collective:
 > https://opencollective.com/nodemon/donate

npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.2 (node_modules\nodemon\node_modules\chokidar\node_modules\fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"})

+ nodemon@2.0.6
added 119 packages from 53 contributors in 11.467s

We have now installed Nodemon.

C:\Users\johnc\workspace4\DemoForRESTAPI>type package.json
{
  "name": "demo",
  "version": "1.0.0",
  "description": "Hands-on for creation of REST API",
  "main": "script.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "John Canessa",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "joi": "^17.3.0"
  }
}

The package.json file contains information about your project. Of interest are the dependencies. Note that each dependency has a version number. The way you express the versions allows your software to use any, a specific version or newer versions.

As you might know, I like to keep things simple (follow the KISS rule). I used to select an IDE based on the programming language I was using. Based on technological advances it seems that VSCode is able to constantly increase the number of programming languages it supports. I have not used it for JavaScript until I started with the code for this post. So far it seems to work fine.

The advantage of using the minimum number (hopefully only one) of IDEs is that you do not have to remember all the idiosyncrasies associated with different IDEs. When using Java VSCode seems to behave well, but on occasions I ran into situations that I switch to Eclipse. On the other hand, I have been developing large amounts of C / C++ code and my IDE of choice is Visual Studio Enterprise Edition. I am currently using version 2019. To be honest with you I have not tried VSCode for C / C++. Perhaps I would be pleasantly surprised.

There is an article named Node.js tutorial in Visual Studio Code that describes how to start using VSCode for JavaScript and in specific Node.js.

C:\Users\johnc\workspace4\DemoForRESTAPI>code .

As usual, if you are in the folder holding your code it is very simple to start the VSCode IDE.

Given that we are developing a server that process a web API, you will need a utility / program that will help you issue HTTP / HTTPS commands to it. Some utilities are command line based (i.e., cURL) while others are UI based (i.e., Postman). Depending on what you are doing different approaches have pros and cons. The best thing is to try and compare them. I have used Postman for a while. It used to be a plug-in / extension to some web browsers. The interface has changed some in the past year or so. I am using Postman on a Windows 10 machine with the Chrome browser. I ran into issues selecting a version of an associated support utility that was not required in older versions of Postman. The version that worked was installed with:  PostmanAgent-win64-0.0.2-Setup.exe.

C:\Users\johnc\workspace4\DemoForRESTAPI>nodemon script.js
[nodemon] 2.0.6
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node script.js`
listening on port 8080 ...

After starting the Node.js server with nodemon, it comes up on the specified port and you are ready to interact with it via a web browser. In my case I used Chrome and the Postman utility also running from Chrome. As previously mentioned, you can make changes to the code and they will be applied as soon as you save them. That way it speeds up the testing and debugging process.

TypeError: Joi.validate is not a function
    at validateBook (C:\Users\johnc\workspace4\DemoForRESTAPI\script.js:94:12)
    at C:\Users\johnc\workspace4\DemoForRESTAPI\script.js:41:19
    at Layer.handle [as handle_request] (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\route.js:137:13)
    at Route.dispatch (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\route.js:112:3)
    at Layer.handle [as handle_request] (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\layer.js:95:5)
    at C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\index.js:281:22
    at Function.process_params (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\index.js:335:12)
    at next (C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\express\lib\router\index.js:275:10)
    at C:\Users\johnc\workspace4\DemoForRESTAPI\node_modules\body-parser\lib\read.js:130:5
^CTerminate batch job (Y/N)?
^C
C:\Users\johnc\workspace4\DemoForRESTAPI>npm i @hapi/joi
npm WARN deprecated @hapi/joi@17.1.1: Switch to 'npm install joi'
npm WARN deprecated @hapi/formula@2.0.0: Moved to 'npm install @sideway/formula'
npm WARN deprecated @hapi/address@4.1.0: Moved to 'npm install @sideway/address'
npm WARN deprecated @hapi/pinpoint@2.0.0: Moved to 'npm install @sideway/pinpoint'
npm WARN demo@1.0.0 No repository field.

+ @hapi/joi@17.1.1
added 6 packages and audited 56 packages in 1.253s
found 0 vulnerabilities

While I was testing the different routes (more on this shortly) I ran into issues with a line that makes a call to a function in the Joi package. After some research on the web, I installed the latest version of Joi. That did not solve the issue. I had to make a simple code change and all was well.

const express = require('express');     // import Express

// !!!! DEPRECATED !!!
// const Joi = require('joi');          // import Joi - used for validation
const Joi = require('@hapi/joi');       // import Joi - used for validation

const app = express();                  // create Express Application on the app variable
app.use(express.json());                // use JSON
 
// **** data for the server (we do not have a DB at this time) ****
const books = [
{title: 'Get Programming with Node.js', id: 1},
{title: 'Hacker\'s Delight', id: 2},
{title: 'Deep Learning', id: 3},
{title: 'Work Deep', id: 4},
{title: 'Head First Java', id: 5}
]

// **** READ request handlers ****
app.get('/', (req, res) => {
    res.send('<h3>Welcome to this REST API demo with Node.js!!!</h3>');
});

// **** get list of books ****
app.get('/api/books', (req,res)=> {
    res.send(books);
});

// **** get book by id ****
app.get('/api/books/:id', (req, res) => {
const book = books.find(c => c.id === parseInt(req.params.id));
 
if (!book)
    res.status(404).send('<h2 style="font-family: Courier; color: red;">Not found!!!</h2>');

res.send(book);
});

// **** CREATE request handler ****
app.post('/api/books', (req, res)=> {
 
const { error } = validateBook(req.body);
if (error){
    res.status(400).send(error.details[0].message)
    return;
}

// **** add book and next available ID ****
const book = {
    id: books.length + 1,
    title: req.body.title
};

books.push(book);

// ???? ????
console.log("create title: " + book.title);

// **** ****
res.send(book);
});
 
// **** UPDATE book by ID request handler ****
app.put('/api/books/:id', (req, res) => {
const book = books.find(c=> c.id === parseInt(req.params.id));
if (!book)
    res.status(404).send('<h2 style="font-family: Courier; color: red;">Not found!!! </h2>');
 
const { error } = validateBook(req.body);
if (error){
    res.status(400).send(error.details[0].message);
    return;
}

// ???? ????
console.log("update title: " + req.body.title);

// **** ****
book.title = req.body.title;
res.send(book);
});

// **** DELETE request handler ****
app.delete('/api/books/:id', (req, res) => {
 
const book = books.find( c=> c.id === parseInt(req.params.id));
if(!book)
    res.status(404).send('<h2 style="font-family: Courier; color: red;"> Not found!!! </h2>');
 
const index = books.indexOf(book);

// **** remove book from DB (array for now) ****
books.splice(index,1);

// ???? ????
console.log("delete title: " + book.title);

// **** ****
res.send(book);
});

// **** ****
function validateBook(book) {
const schema = Joi.object().keys({
    title: Joi.string().min(3).required()
});
return Joi.object(this.schema).validate(book);
}

// **** PORT environment variable ****
const port = process.env.PORT || 8080;
app.listen(port, () => console.log(`listening on port ${port} ...`));

The first few lines are used to import packages / libraries and specify some functionality. We will use the Express.js web application framework in our project.

At this time we will not use a database engine to hold the data. We will be using MongoDB when we work with our storage server, but not now. We will just store our books information in an array. As you can see we have a list of books and an ID associated with each. Chances are that if you develop a real application to track books, you will use a database engine plus additional information per book.

The GET method is used with different URLs to get access to different resources. As we will see towards the end of the source code, our server listens on port 8080. So we can issue a call like:  localhost:8080/ and our server should reply to the client with the welcome message:  Welcome to this REST API demo with Node.js!!!

You can easily test this out by opening a web browser and typing in localhost:8080/. You can also use Postman and be able to get the same result.

If you enter:  localhost:8080/api/books you should get a list of all books in our server. At this moment we should have five books.

If you are interested in the information about a specific book by ID, you can issue the following request:  localhost:8080/api/books/3 and the response should contain only information for the book “Deep Learning” with ID of 3.

The POST method is used to create a new resource in our server. After the new resource is created in the server, we should be able to get it from our client software. We will see these and the other two methods in use shortly.

We can use the PUT method to update a resource. In this case we could update the title for a book in our server.

Finally we could use the DELETE method to delete from our server a specific book by ID. The ID can refer to any book title in our server.

The validateBook() function allows us to validate the data being received by our server. In this case we are validating the title to contain at least three characters.

The last couple lines in our code configure our server to listen on port 8080 on our host computer.

Postman -> GET - localhost:8080/

<h3>Welcome to this REST API demo with Node.js !!!</h3>


Postman -> GET - localhost:8080/api/books

[
    {
        "title": "Get Programming with Node.js",
        "id": 1
    },
    {
        "title": "Hacker's Delight",
        "id": 2
    },
    {
        "title": "Deep Learning",
        "id": 3
    },
    {
        "title": "Work Deep",
        "id": 4
    },
    {
        "title": "Head First Java",
        "id": 5
    }
]


Postman -> POST - localhost:8080/api/books/ -
	{
		"title": "Dr. No"
	}

	{
		"id": 6,
		"title": "Dr. No"
	}


Postman -> GET - localhost:8080/api/books

[
    {
        "title": "Get Programming with Node.js",
        "id": 1
    },
    {
        "title": "Hacker's Delight",
        "id": 2
    },
    {
        "title": "Deep Learning",
        "id": 3
    },
    {
        "title": "Work Deep",
        "id": 4
    },
    {
        "title": "Head First Java",
        "id": 5
    },
    {
        "id": 6,
        "title": "Dr. No"
    }
]


Postman -> PUT - localhost:8080/api/books/6 -

	{
		"title": "Dr. No by Ian Fleming"
	}
	
	{
    "id": 6,
    "title": "Dr. No by Ian Fleming"
	}


Postman -> GET - localhost:8080/api/books

[
    {
        "title": "Get Programming with Node.js",
        "id": 1
    },
    {
        "title": "Hacker's Delight",
        "id": 2
    },
    {
        "title": "Deep Learning",
        "id": 3
    },
    {
        "title": "Work Deep",
        "id": 4
    },
    {
        "title": "Head First Java",
        "id": 5
    },
    {
        "id": 6,
        "title": "Dr. No by Ian Fleming"
    }
]


Postman -> DELETE - localhost:8080/api/books/6

{
    "id": 6,
    "title": "Dr. No by Ian Fleming"
}

Postman -> GET - localhost:8080/api/books/

[
    {
        "title": "Get Programming with Node.js",
        "id": 1
    },
    {
        "title": "Hacker's Delight",
        "id": 2
    },
    {
        "title": "Deep Learning",
        "id": 3
    },
    {
        "title": "Work Deep",
        "id": 4
    },
    {
        "title": "Head First Java",
        "id": 5
    }
]

We will now use Postman to exercise most of the methods we just cover going over the source code. You could use any other tool besides Postman i.e., RestMan and get the same results.

Note that GET methods can be used directly on any web browser. Using the other three methods tends to require additional tools.

We issue a properly formatted get request to our server and it returns a list of all 5 books with their associated IDs. The response is formatted in JSON as expected.

We then issue a properly formatted POST request to create a new book resource in our server. The book “Dr. No” with ID 6 was created.

Next we issue a properly formatted GET request to get a list of all the books in our server. We can see that we now have six books.

We now issue a properly formatted PUT request to edit the resource with ID 6. We would like to include the name of the author next to the title.

We issue a properly formatted GET request and see that book with ID 6 contains the book name and author.

We now issue a properly formatted DELETE request to delete the book with ID 6.

Finally we issue a properly formatted GET request and get back a list of all five books remaining in the server.

Hope you enjoyed experimenting with this code. Enhance it as needed. The entire code for this project can be found in my GitHub repository.

If you have comments or questions regarding this, or any other post in this blog, or if you would like for me to help out with any phase in the SDLC (Software Development Life Cycle) of a project associated with a product or service, please do not hesitate and leave me a note below. If you prefer, send me a private e-mail message. I will reply as soon as possible.

Keep on reading and experimenting. It is the best way to learn, become proficient, refresh your knowledge and enhance your developer toolset!

One last thing, many thanks to all 3,570 subscribers to this blog!!!

Keep safe during the COVID-19 pandemic and help restart the world economy.

John

E-mail:  john.canessa@gmail.com

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.