RESTful Web Services Example : ExpressJS + Node.js + MongooseJS
In my previous articles I have explaines about CRUD operations Node.js + MongoDB – Performing CRUD Operations and Mongoose – Node.js + MongoDB with Mongoose Tutorial. In this post I will show you how to create CRUD operations using RESTful Web Services and ExpressJS with MongooseJS. I would be using the following 3 Node packages for this sample namely:
ExpressJs
From the Node Package manager website:
Fast, unopinionated, minimalist web framework
Express is used to create Web applications using Node.js. It provides support for routes, setting up view templates and others. This is most popular Node.js web framework as of now. The advantage of using ExpressJS is to reduce the time and lines of code written unless using the plain Node.js modules.
Body-parser
From the Node Package manager wwebsite:
Node.js body parsing middleware.
This middleware is used for intercepting the requests, parsing the request body and then populating it in the node.js’s req
object.
MongooseJS
From the Node Package Manager website:
MongooseJS is a MongoDB object modeling tool designed to work in an asynchronous environment.
This is used to define Model objects and then use it to interact with MongoDB, a kind of ORM in the OO Programming world. MongooseJS is a Object Data Mapping (ODM) for JavaScript frameworks. This helps you to map the JavaScript objects to the MongoDB database. This tool is developed and maintained by the MongoDB community itself.
Defining the RESTful API
In this tutorial I am going to implement a simple RESTful Web Services (Read : Spring REST Web Services) example using ExpressJS ,Node.js and MongooseJS. The right below section just defines the list of operations that are going to implemented as part of the excersise. The list of RESTful APis are :
- Fetch list of all books in the system : GET /book/
- Fetch details of a book in the system : GET /book/:isbn
- Add a new book to the system : POST /book/
- Edit details of existing book in the system : PUT /book/:isbn
- Delete given book from the system : DELETE /book/:isbn
1. Fetch list of all books in the system : GET /book/
Parameters: No parameters
Response: The response is going to be an array of JSON objects (Also Read : What is JSON?) with the structure given below.
[ { "_id":STRING, "name":STRING, "isbn":STRING, "author":STRING, "pages":INTEGER, "_v":STRING } ]
2. Fetch details of a book in the system : GET /book/:isbn
Parameters: ISBN of the book
Response: The response is going to be the JSON object with the structure given below.
{ "_id":STRING, "name":STRING, "isbn":STRING, "author":STRING, "pages":INTEGER, "_v":STRING }
3. Add a new book to the system : POST /book/
Parameters: Data of following format has to be sent in the request body
{ "name":STRING, "isbn":STRING, "author":STRING, "pages":INTEGER }
Response: The response is going to be the JSON object with the structure given below.
{ "messaage": "Successfully added book", "book": { "__v": STRING, "name": STRING, "isbn": STRING, "author": STRING, "pages": INTEGER, "_id": STRING } }
4. Edit details of existing book in the system : PUT /book/:isbn
Parameters: ISBN of the book and the updated book information in the below format in request body.
{ "name":STRING, "isbn":STRING, "author":STRING, "pages":INTEGER }
Response: The response is going to be the JSON object with the structure given below.
{ "messaage": "Successfully updated the book", "book": { "__v": STRING, "name": STRING, "isbn": STRING, "author": STRING, "pages": INTEGER, "_id": STRING } }
5. Delete given book from the system : DELETE /book/:isbn
Parameter: ISBN of the book to be deleted
Response: The response is going to be the JSON object with the structure given below.
{ "messaage": "Successfully deleted the book", "book": { "__v": STRING, "name": STRING, "isbn": STRING, "author": STRING, "pages": INTEGER, "_id": STRING } }
Implementing the RESTful API : ExpressJS + Body-parser + Node.js
As mentioned earlier I would be using express.js, node.js and body-parser.js node packages with MongooseJS. The below code sets up the packages and starts the web server on port 3300.
//Express is required for creating Node.js based web apps var express = require('express'); //body-parser is used to parse the Request body and populate the req. var bodyParser = require('body-parser'); //mongoose is used for interacting with MongoDB var mongoose = require('mongoose'); var app = express(); app.set('port', 3300); //Configuring Express App to make use of BodyParser's JSON parser to parse //JSON request body app.use(bodyParser.json()); var dbHost = 'mongodb://localhost:27017/test'; mongoose.connect(dbHost); //Create a schema for Book var bookSchema = mongoose.Schema({ name: String, //Also creating index on field isbn isbn: {type: String, index: true}, author: String, pages: Number }); //Create a Model by using the schema defined above //Optionally one can provide the name of collection where the instances //of this model get stored. In this case it is "mongoose_demo". Skipping //this value defaults the name of the collection to plural of model name i.e books. var Book = mongoose.model('Book', bookSchema); //Connecting to Mongod instance. mongoose.connection; //Starting up the server on the port: 3300 app.listen(app.get('port'), function(){ console.log('Server up: http://localhost:' + app.get('port')); });
You can save the above code in file: express_demo.js and then run it using node express_demo.js
, you should be able to see the below output:
Server up: http://localhost:3300
Implementing GET /book
The below code implements this API:
//Get all the books app.get('/book', function(req, res){ //Find all the books in the system. Book.find({}, function(err, result){ if ( err ) throw err; //Save the result into the response object. res.json(result); }); });
In ExpressJS we create an application instance using express()
(as seen above) and then setup the routes using the application instance. For example, for a GET call we use applicationInstance.get("ROUTE", handler(req, res){})
, for a POST call we would set it up as applicationInstance.post("ROUTE", handler(req, res){})
and so on for other HTTP Methods like PUT, DELETE and others.
Implementing GET /book/:isbn
The below code is the implementation of the API
//Get the details of the book with the given isbn app.get('/book/:isbn', function(req, res){ console.log("Fetching details for book with ISBN: " + req.params.isbn); //The parameter in the route is accessed via request.params object. Book.findOne({isbn: req.params.isbn}, function(err, result){ if ( err ) throw err; res.json(result); }); });
In this Implementation the following are points of observation:
- Creating parameterized routes by using the :variable_Name in the route definition. In this case I have defined the route as
/book/:isbn
and this matches any url of form /book/abc123 - Accessing the path variable in the route via the request.params object. In this case I retrieve the ISBN string passed in the URL via
req.params.isbn
- Using findOne with query object. In this case I am making use of MongoDB’s findOne() API along with the query object which is built using the ISBN information passed to retrieve one book information matching the ISBN
Implementing POST /book
This API is used to add a new book to the system. The information of the book is to be passed in the request body and we make use of the body-parser module to parse the json in the request body and populate it in the Node’s request
object. The below code implements this API
//Add a new book app.post("/book", function(req, res){ console.log("Adding new Book: " + req.body.name); var book = new Book({ name:req.body.name, isbn: req.body.isbn, author: req.body.author, pages: req.body.pages }); //Saving the model instance to the DB book.save(function(err, result){ if ( err ) throw err; //After successfully saving the book we generate a JSON response with the //message and the inserted book information. res.json({ messaage:"Successfully added book", book:result }); }); });
In this implementation we read the JSON data passed by the Client, populate our instance of Book
model class and persist the same to the DB using Mongoose API.
Implementation PUT /book/:isbn
This implementation is similar to GET /book/:isbn in terms of parameter handling and POST /book in terms of handling the book data passed from the client. We query for the existing information in the DB and then populate this existing data with the data passed from the client.
//Update an existing book app.put("/book/:isbn", function(req, res){ Book.findOne({isbn: req.params.isbn}, function(err, result){ if ( err ) throw err; if(!result){ res.json({ message:"Book with ISBN: " + req.params.isbn+" not found.", }); } result.name = req.body.name; result.isbn = req.body.isbn; result.author = req.body.author; result.pages = req.body.pages; result.save(function(err, result){ if ( err ) throw err; res.json({ message:"Successfully updated the book", book: result }); }); }); });
Implementing DELETE /book/:isbn
Again this implementation is similar to GET /book/:isbn in terms of parameter handling and finding the book from the DB. To delete the book information we make use of findAndRemove
API in Mongoose.
//Delete an existing book app.delete("/book/:isbn", function(req, res){ Book.findOneAndRemove({isbn: req.params.isbn}, function(err, result){ res.json({ message: "Successfully deleted the book", book: result }); }); });
Testing the REST APIs
Now that I have shown you the implementation of individual APIs, lets go ahead and test the implemented APIs. For testing POST, PUT, DELETE I am making use of Postman Client’s plugin for Chrome.
GET /book
GET /book/:isbn
POST /book
PUT /book/:isbn
DELETE /book/:isbn
Wrapping Up
I have shown you how the REST API is designed and then implemented using ExpressJS, Node.js and MongooseJS. For those looking for complete solution, copy the below code into a file named: express_demo.js
//Saved in file: express_demo.js //Express is required for creating Node.js based web apps var express = require('express'); //body-parser is used to parse the Request body and populate the req. var bodyParser = require('body-parser'); //mongoose is used for interacting with MongoDB var mongoose = require('mongoose'); var app = express(); app.set('port', 3300); //Configuring Express App to make use of BodyParser's JSON parser to parse //JSON request body app.use(bodyParser.json()); var dbHost = 'mongodb://localhost:27017/test'; mongoose.connect(dbHost); //Create a schema for Book var bookSchema = mongoose.Schema({ name: String, //Also creating index on field isbn isbn: {type: String, index: true}, author: String, pages: Number }); //Create a Model by using the schema defined above //Optionally one can provide the name of collection where the instances //of this model get stored. In this case it is "mongoose_demo". Skipping //this value defaults the name of the collection to plural of model name i.e books. var Book = mongoose.model('Book', bookSchema); //Connecting to Mongod instance. mongoose.connection; //Starting up the server on the port: 3300 app.listen(app.get('port'), function(){ console.log('Server up: http://localhost:' + app.get('port')); }); //Get the details of the book with the given isbn app.get('/book/:isbn', function(req, res){ console.log("Fetching details for book with ISBN: " + req.params.isbn); Book.findOne({isbn: req.params.isbn}, function(err, result){ if ( err ) throw err; res.json(result); }); }); //Get all the books app.get('/book', function(req, res){ //Find all the books in the system. Book.find({}, function(err, result){ if ( err ) throw err; //Save the result into the response object. res.json(result); }); }); //Add a new book app.post("/book", function(req, res){ console.log("Adding new Book: " + req.body.name); var book = new Book({ name:req.body.name, isbn: req.body.isbn, author: req.body.author, pages: req.body.pages }); //Saving the model instance to the DB book.save(function(err, result){ if ( err ) throw err; res.json({ messaage:"Successfully added book", book:result }); }); }); //Update an existing book app.put("/book/:isbn", function(req, res){ Book.findOne({isbn: req.params.isbn}, function(err, result){ if ( err ) throw err; if(!result){ res.json({ message:"Book with ISBN: " + req.params.isbn+" not found.", }); } result.name = req.body.name; result.isbn = req.body.isbn; result.author = req.body.author; result.pages = req.body.pages; result.save(function(err, result){ if ( err ) throw err; res.json({ message:"Successfully updated the book", book: result }); }); }); }); //Delete an existing book app.delete("/book/:isbn", function(req, res){ Book.findOneAndRemove({isbn: req.params.isbn}, function(err, result){ res.json({ message: "Successfully deleted the book", book: result }); }); });
Before running this you would have to install the following packages: express, body-parser, mongoose using the command npm install
.