Building a Node.js API with Hapi.js and MongoDB
Tags: opensource, javascript
Published: 2021-08-26 15:33:33

As our web applications scale, there is a need to reduce the development time by the use of a more reliable, and salable, tool, which gets the job done much faster.
In this tutorial, we will build a Book Store API with Hapi.js and MongoDB. We will set up MongoDB, Hapi.js and build a RESTful API.

## **Prerequisites**

This tutorial will be a hands-on demonstration, you can find the source code on [Gitbub](https://github.com/Claradev32/BookStore.git). If you'd like to follow along, be sure you have the following:

1. [MongoDB](https://www.mongodb.com/cloud/atlas) Database - This tutorial uses MongoDB.
2. [Node.js](https://nodejs.dev/) - This tutorial uses Nodjs and NPM 
3. [Postman](https://www.postman.com/downloads/) - This tutorial uses Postman to test the API


## What is Hap.js

Hapi.js is a Nodejs framework used to build powerful, scalable applications, with minimal overhead and full out-of-the-box functionality. Hapi.js was originally developed to handle Walmart’s Black Friday scale, hapi continues to be the proven choice for enterprise-grade backend needs.


## Get started

To get started to create a folder for your project and access it from your terminal. Open the folder in Visual Studio Code or any other IDE you prefer.


    mkdir BookApp //create a folder for the project
    cd \BookApp // change directory to the app folder

Then initialize NPM, which will create a package.json file in our app root directory where we store our dependencies.


    npm init -y

Next, we need to install some packages for our project.


    npm install @hapi/Hapi mongoose dotenv morgan --save 

Next, let's take a look at our project structure.

![Project Structure](https://paper-attachments.dropbox.com/s_0FEADB409A157CAA8DD14F2CA9AC086842E68BDBD81AF897CA3A753F47922E22_1629990892959_image.png)












- Controllers - is where we handle the application logic
- Model - is where we handle our database collection
## Setting Up Hapi.js Server

We will start by creating a **server.js** and ****file. Which is our application entry point.
In your project root directory create and **server.js** file, then import the Hapi package we installed in the previous section, create an instance of the Hapi server, specify the **port** you want the server to listen to, and **host** address**.** 
Next, we will create the root route, of our application with a GET request and create a **handler.** For now, we will be sending a simple text to the client. 
Finally, we start the Hapi.js server with the **start** method and display a message to the console.

Add this code to your **server.js**

    const Hapi = require('@hapi/hapi');
    
    const server = Hapi.server({
      port: 4000,
      host: '127.0.0.1'
    });
    
    server.route({
      method: 'GET',
      path: '/',
      handler: (req, h) => {
    
        return 'Hello I am Hapi!<';
      }
    });
    
    server.start();
    console.log(`Server running on port ${server.info.uri}`);

It's to start our server. run this command on your command line to start the server.


    node server.js

Next, open your web browser, navigate to **localhost:4000.** The server will respond with **Hello I am Hapi!.**


## Creating a MongoDB Database

Now that you have learned how to create a Hapi server, let's go ahead and set up MongoDB for our project.
We will be using [Mongoose](https://mongoosejs.com/) to communicate with the MongoDB database.
Run this command to install Mongoose:


    npm install mongoose

Next, let’s connect MongoDB to our application using Mongoose. We will require mongoose, create a connection using the mongoose connect method and pass in our local server address to the server.
If the database is up, you should see “DB started” on the console.
Add this code to your server.js:


    const mongoose = require("mongoose");
    mongoose
     .connect("mongodb://localhost:27017/BookStore")
     .then(() => {
      console.log("db started!");
     })
     .catch((e) => {
      console.log(e);
     });


## Creating Book Schema

We now have our database up and running, let’s go ahead and create a book [schema](https://mongoosejs.com/docs/guide.html). Each book in our store will have a **name**, **author,** **price**, **supplier**, and **createdAt.** 
Next, we define the type of data the documents should accept, then we validate the data from the users, to make sure they don’t submit empty records.
Add this code to **model/BookModel:**


    const mongoose = require("mongoose");
    const BookSchema = new mongoose.Schema({
     name: {
      type: String,
      required: [true, "Book price is required"],
     },
     author: {
      type: String,
      required: [true, "Authors name is required"],
     },
     price: { type: Number, required: [true, "Book price is required"] },
     ISBN: {
      type: String,
      required: [true, "Book ISBN is required"],
     },
     supplier :{
      type: String,
      required: [true, "Suppliers name is required"],
     },
     createdAt: {
      type: Date,
      default: Date.now(),
     },
    });
    module.exports = mongoose.model("Books", BookSchema);


## Creating Books

Now that we have defined our database schema. let’s go ahead and add a handler to add books to the store.
First, we require the Books model, then create and export our handler function.

Next, we create an object from the book model and add a record of the collection using the **create** method. The data from the form is stored in the **payload** as [JSON](https://www.w3schools.com/whatis/whatis_json.asp#:~:text=JSON%20stands%20for%20JavaScript%20Object,server%20to%20a%20web%20page), so we need to convert it to an object using the JSON parse method.
Lastly, we return the created document as a response to the users.

Add this code to **controller/BookControllers.**


    exports.createBook = async (req, h) => {
     const data = await Books.create(JSON.parse(req.payload));
     return status;
    };

Now lets go ahead and create a route for this handler function.
add this code to your server.js


    server.route({
     method: "GET",
     path: "/api/store",
     handler: controllers.getAllBooks,
    });
## Getting All Books

Now that we can now add books to our database, let’s get the books stored in the database.
First, we get the books from the form, which is stored as [JSON](https://www.w3schools.com/whatis/whatis_json.asp#:~:text=JSON%20stands%20for%20JavaScript%20Object,server%20to%20a%20web%20page) in the payload. Then we need to convert the data to an object since we store data as objects to our collection.

Add this code to **controller/BookController:**


    exports.getAllBooks = async (req, h) => {
     const books = await Books.find();
     return books;
    };

Now let's go ahead and create a route for this handler function.
Add this code to **server.js.**


    server.route({
     method: "POST",
     path: "/api/store",
     handler: controllers.createBook,
    });
## Getting a Book

Let's go now get a book by its id. 
First, we need the **id** of the Book selected, then we use [object destructing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) to the id from the **params** object.
Next, we query the book collection to get a book by its id, using the **findById** method. Then now return the book to the client.

Add this code to **controller/BookController:**


    exports.getBook = async(req, h) => {
     const {id} = req.params
     const book = await Books.findById({_id:id});
     return book;
    };

Next, let’s go ahead and create the **getBook** route with a patch request with an id parameter.

Add this code to **server.js:**


    server.route({
     method: "GET",
     path: "/api/store/{id}",
     handler: controllers.getBook,
    }
## Updating a Book

Now, let create a handler function to update the book in our collection.
First, we need to get the book id from the params object, also get the update data from the request payload. 
Next, we create an object from the books model and use the findByIdAndUpdate method to update the book whose id is specified in the request parameters. Then convert the payload to an object and pass it as the new value of the document.

Next, we return the updated document with the **new** option set to true and send the document to the client.

Add this code to **controller/BookController:**


    exports.updateBook = async (req, h) => {
     const { id } = req.params;
     const data = JSON.parse(req.payload);
     const modifiedBook = await Books.findByIdAndUpdate({ _id: id }, data, {
      new:true,
     });
     return modified book;
    }

Next, let’s go ahead and create the update route with a patch request and an id parameter.
Add this code to **server.js:**

    server.route({
     method: "PATCH",
     path: "/api/store/{id}",
     handler: controllers.updateBook,
    })
## Deleting a Book

Let's go ahead and create our delete handler. 
First, we need to get the book id from the params object, then delete the book from the collection from the id.
Next, we return null to the client, since the record no longer exists.

Add this code to **controller/BookController.js:**

    exports.deleteBook = async (req, h) => {
     const { id } = req.params;
     await Books.findByIdAndDelete({ _id: id });
     return "null";
    };

Next, let’s go ahead and create the delete route, which will listen to a delete request, then we pass an id as a parameter.

Add this code to **server.js:**


    server.route({
     method: "DELETE",
     path: "/api/store/{id}",
     handler: controllers.deleteBook,
    })


## Testing our API

Now that we have implemented our RESTful API, let us go ahead and test them.
Open Postman, test the with these endpoints.

- [http://localhost:3000/api/store/](http://localhost:3000/api/store/) - GET request
- [http://localhost:3000/api/store](http://localhost:3000/api/store) - POST request with this request body


{
      "name": "Advanced Javacript",
      "price": 2000,
      "ISBN" : "AD90976",
      "supplier": "mark James",
      "author": "james"
  }


- [http://localhost:3000/api/store/](http://localhost:3000/api/store/){id} - GET request
- [http://localhost:3000/api/store/](http://localhost:3000/api/store/){id} - PATCH request
- [http://localhost:3000/api/store/](http://localhost:3000/api/store/){id} - DELETE request


## Conclusion

Throughout this tutorial, you've learned how to create a RESTful API using Hapi.js. You've also experienced setting up MongoDB and testing API using Postman.
Now, how would you use Hapi in your next project to create APIs?