Skip to content
Ruminations
GitHubLinkedin

RESTful API with Node.js, Express and MongoDB

NodeJS, Web Development, MongoDB, Express, REST API10 min read

Build a RESTful API with Node.js, Express and MongoDB

In building applications, an API (application programming interface) simplifies programming by abstracting the underlying implementation and only exposing objects or actions the developer needs. Representational state transfer (REST) is a software architectural style which uses a subset of HTTP. It is commonly used to create interactive applications that use Web services. A Web service that follows these guidelines is called RESTful. Such a Web service must provide its Web resources in a textual representation and allow them to be read and modified with a stateless protocol and a predefined set of operations. This approach allows interoperability between the computer systems on the Internet that provide these services. REST is an alternative to, for example, SOAP as way to access a Web service.

Overview

In this tutorial, we’ll be building a RESTful CRUD (Create, Retrieve, Update, Delete) API with Node.js, Express and MongoDB. We’ll use Mongoose for interacting with the MongoDB instance. Along the way, you will be able to leverage and improve your JS skills. Developers can also expand this tutorial by using MySQL instead of MongoDB or securing API by adding authentication callbacks.

Introduction

Express is one of the most popular web frameworks for node.js. It is built on top of Node.js http module, and adds support for routing, middleware, view system etc. It is very simple and minimal, unlike other frameworks that try do way to much, thereby reducing the flexibility for developers to have their own design choices.

Mongoose is an ODM (Object Document Mapping) tool for Node.js and MongoDB. It helps you convert the objects in your code to documents in the database and vice versa.

The Goal

In this tutorial, we will be building a simple book-shelf application. We will build Rest APIs for creating, listing, editing and deleting a book.

We’ll start by building a simple web server and then move on to configuring the database, building the book model and different routes for handling all the CRUD operations.

Finally, we’ll test our REST APIs using Curl.

Before you Start

Install Node.js and MongoDB on your machine. Then, verify that you have Node.js, NPM and MongoDB installed on our machine. To do that, open your terminal or command prompt and run

node -v
npm -v
mongo --version

You also need a code editor like Visual Studio Code, Sublime Text or Notepad++;


Part 1 - Project Initialization

Create an application folder where your application will reside. To initialize the application, you need to create a package.json file, which will hold all the metadata for the node application. The file allows npm to handle installing the package dependencies and scripts we write to handle the application. Assuming that you’ve Node.js installed, we can initialize the application from the command line by executing the following:

npm init

Follow the wizard as below to setup your app with a package.json file.

package name: (nodejs-mongo-app)
version: (1.0.0)
description: Rest API demo using node.js,express and MongoDB by Vijeth P H.
entry point: (index.js) server.js
test command:
git repository:
keywords: RestAPI Express Node MongoDB Books
author: vijethph
license: (ISC) MIT
About to write to /home/vijethph/Desktop/node-express-mongo-app/package.json:
{
"name": "nodejs-mongo-app",
"version": "1.0.0",
"description": "Rest API demo using node,express and MongoDB by Vijeth P H.",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"RestAPI",
"Express",
"Node",
"MongoDB",
"Books"
],
"author": "vijethph",
"license": "MIT"
}
Is this OK? (yes) yes

At this point, you should verify that you have a package.json file is available in your project root by listing the files with either ls -l or dir depending on your OS.

We need a file that can act as command central for our application. This file will be executed first by npm when we ask it to run our application. This file can have object instances of multiple modules that we write as well as third party modules that we install from the npm directory. These modules are dependencies of the project. We’ll install a few of these dependencies now:

npm install express body-parser mongoose cors --save

express is a web framework that is used for building the REST APIs. body-parser is a module that parses the request (of various content types) and creates a req.body object that we can access in our routes. cors provides Express middleware to enable CORS with various options.


Part 2 - Setting up the web server

Let’s now create the main entry point of our application. Create a new file named server.js in the root folder of the application with the following contents:

//server.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
// create express app
const app = express();
// create cors origin and use it
var corsOptions = {
origin: "http://localhost:8080",
};
app.use(cors(corsOptions));
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// parse requests of content-type - application/json
app.use(bodyParser.json());
// define a simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to Bookshelf app!!" }).end();
});
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});

Run the server with the command node server.js. Open your web browser with URL http://localhost:8080 to see the output.


Part 3 - Database Configuration

Create a new folder config in the root folder of our application for keeping all the configurations. Create a new file database.config.js inside config folder with the following contents:

//database.config.js
module.exports = {
url: "mongodb://localhost:27017/bookshelf",
};

We’ll now import the above database configuration in server.js and connect to the database using mongoose.

Add the following code to the server.js file after app.use(bodyParser.json()); line:

//book.model.js
// Configuring the database
const dbConfig = require("./config/database.config.js");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
// Connecting to the database
mongoose
.connect(dbConfig.url, {
useNewUrlParser: true,
})
.then(() => {
console.log("Successfully connected to the database");
})
.catch((err) => {
console.log("Could not connect to the database. Exiting now...", err);
process.exit();
});

Next, we will define the book model. Create a new folder called app inside the root folder of the application, then create another folder called models inside the app folder. Now, create a file called book.model.js inside app/models folder with the following contents:

const mongoose = require("mongoose");
const BookSchema = mongoose.Schema({
title: String,
summary: String,
});
module.exports = mongoose.model("Book", BookSchema);

The Book model is very simple. It contains a title and a summary field. This Mongoose Model represents books collection in MongoDB database.


Part 4 - Define the controller

The controller will contain methods for handling all the CRUD operations. Create a new folder called controllers inside the app folder, then create a new file called book.controller.js inside app/controllers folder with the following contents :

//book.controller.js
const book = require("../models/book.model.js");
// Create and Save a new book
exports.create = (req, res) => {};
// Retrieve and return all books from the database.
exports.findAll = (req, res) => {};
// Find a single book with a bookId
exports.findOne = (req, res) => {};
// Update a book identified by the bookId in the request
exports.update = (req, res) => {};
// Delete a book with the specified bookId in the request
exports.delete = (req, res) => {};

Let us implement all of the above controller functions one by one:

Creating a new book

// Create and Save a new book
exports.create = (req, res) => {
// Validate request
if (!req.body.summary) {
return res.status(400).send({
message: "book summary can not be empty",
});
}
// Create a book
const book = new book({
title: req.body.title || "Untitled Book",
summary: req.body.summary,
});
// Save book in the database
book
.save()
.then((data) => {
res.send(data);
})
.catch((err) => {
res.status(500).send({
message: err.message || "Some error occurred while creating the book.",
});
});
};

Retrieving all books

// Retrieve and return all books from the database.
exports.findAll = (req, res) => {
book
.find()
.then((books) => {
res.send(books);
})
.catch((err) => {
res.status(500).send({
message: err.message || "Some error occurred while retrieving books.",
});
});
};

Retrieving a single book

// Find a single book with a bookId
exports.findOne = (req, res) => {
book
.findById(req.params.bookId)
.then((book) => {
if (!book) {
return res.status(404).send({
message: "book not found with id " + req.params.bookId,
});
}
res.send(book);
})
.catch((err) => {
if (err.kind === "ObjectId") {
return res.status(404).send({
message: "book not found with id " + req.params.bookId,
});
}
return res.status(500).send({
message: "Error retrieving book with id " + req.params.bookId,
});
});
};

Updating a book

// Update a book identified by the bookId in the request
exports.update = (req, res) => {
// Validate Request
if (!req.body.summary) {
return res.status(400).send({
message: "book summary can not be empty",
});
}
// Find book and update it with the request body
book
.findByIdAndUpdate(
req.params.bookId,
{
title: req.body.title || "Untitled book",
summary: req.body.summary,
},
{ new: true }
)
.then((book) => {
if (!book) {
return res.status(404).send({
message: "book not found with id " + req.params.bookId,
});
}
res.send(book);
})
.catch((err) => {
if (err.kind === "ObjectId") {
return res.status(404).send({
message: "book not found with id " + req.params.bookId,
});
}
return res.status(500).send({
message: "Error updating book with id " + req.params.bookId,
});
});
};

The { new: true } option in the findByIdAndUpdate() method is used to return the modified document to the then() function instead of the original.

Deleting a book

// Delete a book with the specified bookId in the request
exports.delete = (req, res) => {
book.findByIdAndRemove(req.params.bookId)
.then(book => {
if(!book) {
return res.status(404).send({
message: "book not found with id " + req.params.bookId
});
}
res.send({message: "book deleted successfully!"});
}).catch(err => {
if(err.kind === 'ObjectId' || err.name === 'NotFound') {
return res.status(404).send({
message: "book not found with id " + req.params.bookId
});
}
return res.status(500).send({
message: "Could not delete book with id " + req.params.bookId
});
});
};

Part 5 - Define the routes

When a client sends request for an endpoint using HTTP request (GET, POST, PUT, DELETE), we need to determine how the server will reponse by setting up the routes.

Create a new folder called routes inside the app folder. Now, create a new file called book.routes.js inside app/routes folder with the following contents:

//book.routes.js
module.exports = (app) => {
const books = require("../controllers/book.controller.js");
// Create a new book
app.post("/books", books.create);
// Retrieve all books
app.get("/books", books.findAll);
// Retrieve a single book with bookId
app.get("/books/:bookId", books.findOne);
// Update a book with bookId
app.put("/books/:bookId", books.update);
// Delete a book with bookId
app.delete("/books/:bookId", books.delete);
};

Include the routes in server.js. Add the following require statement before app.listen() line inside server.js file (right before app.listen()):

// Require books routes
require("./app/routes/book.routes.js")(app);

Part 6 - Test the APIs

Run our Node.js application with command: node server.js. Open the terminal and create a new Book with the following command:

curl -X POST \
-H 'content-type:application/json' \
-d '{"title":"Sunshine","summary":"A story about weather"}' \
http://localhost:8080/books

If you do not receive any errors, you’ll see the book record for Sunshine added into MongoDB database of bookshelf and under collection books. Feel free to add more records to your database. You can also perform these testing of APIs with Postman app.

Get all Books in database with the following command:

curl -X GET http://localhost:8080/books

Conclusion

In this tutorial, we’ve learned how to develop a Node.js REST API and connect it with MongoDB to fetch and manipulate the data. From here, you can take different steps. For instance, you can expand this project by adding more features like data validation to it. You can also improve the API security and scalability by following Node.js best practices. Lastly, you can try this tutorial with MySQL database or another server-side coding language like PHP or ASP.NET.

Credits & Attributions