RESTful API with Node.js, Express and MongoDB
— NodeJS, Web Development, MongoDB, Express, REST API — 10 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 -vnpm -vmongo --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.jstest command:git repository:keywords: RestAPI Express Node MongoDB Booksauthor: vijethphlicense: (ISC) MITAbout 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 appconst app = express();
// create cors origin and use itvar corsOptions = { origin: "http://localhost:8080",};app.use(cors(corsOptions));
// parse requests of content-type - application/x-www-form-urlencodedapp.use(bodyParser.urlencoded({ extended: true }));
// parse requests of content-type - application/jsonapp.use(bodyParser.json());
// define a simple routeapp.get("/", (req, res) => { res.json({ message: "Welcome to Bookshelf app!!" }).end();});
// set port, listen for requestsconst 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 databaseconst dbConfig = require("./config/database.config.js");const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
// Connecting to the databasemongoose .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 bookexports.create = (req, res) => {};
// Retrieve and return all books from the database.exports.findAll = (req, res) => {};
// Find a single book with a bookIdexports.findOne = (req, res) => {};
// Update a book identified by the bookId in the requestexports.update = (req, res) => {};
// Delete a book with the specified bookId in the requestexports.delete = (req, res) => {};
Let us implement all of the above controller functions one by one:
Creating a new book
// Create and Save a new bookexports.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 bookIdexports.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 requestexports.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 requestexports.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 routesrequire("./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
- Building a RESTful API Using Node.JS and MongoDB by Kaustubh Ghadge and Matt Zand
- How To Build Simple RESTful API With Node.js, ExpressJs And MongoDb by David Inyang-Etoh
- Node.js, Express & MongoDb: Build a CRUD Rest Api example by Bezkoder
- Building a Restful CRUD API with Node.js, Express and MongoDB by Rajeev Singh
- Wikipedia