This tutorial explains how to modullarizing Node.js and Express.js applications. I am using the existing code from my previous post and explaining how to write a modular Node.js applications.
When it comes to writing the modular Node.js applications, there isn’t any one better way to write the modular applications. It comes through your experience and you may encounter different way to re-organize the code and every time it may look better approach for you. In this tutorial, I am going to explain which is better approach from my experience with Express.js and Node.js.
I have shown you how to build RESTful APIs using Express.js and Node.js here. In this post I will show you how we can modularize the code i.e keep related parts of the code in single Javascript file. For example, code dealing with DB can be placed in one javascript file, the code dealing with handling the requests can be placed in another Javascript file.
Writing Modular Node.js Applications
I am going to create the following modules from the example in this post:
- Application Start point
- Database Access Module
- Controller module
Basic Introduction to Modularization in Node.js
Before I go a bit advanced into modularization of Node.js applications, I would like to take a very simple example to explain the basic concept behind modularizing code in Node.js. I will write a simple calculator app which supports: Addition, Subtraction, Multiplication. I will create a module for doing the calculation and another one which actually runs the application. So there would be 2 Javascript files namely: calculator.js and app.js.
Lets look at the calculator.js:
//File name: calculator.js //Use module.exports to export any function to be available via require() module.exports.add = function(a,b){return a+b;} module.exports.subtract = function(a,b){return a-b;} module.exports.multiply = function(a,b){return a*b;}
module
is an Object in Node.js used to create Modules. We make the methods in calculator.js available by assigning them to the exports
property of module object. We now have a module for performing calculations.
Note: Interesting read about difference between module.exports and exports.
Now lets use this module in our app.js which is the entry point for this simple sample to demonstrate basic concept behind modularity.
var calculator = require("./calculator"); console.log("Sum of 4,5: "+ calculator.add(4,5)); console.log("Difference between 4,5: "+ calculator.subtract(4,5)); console.log("Product of 4,5: "+ calculator.multiply(4,5));
Executing the above we would see something like below:
With this basic introduction to Modularity in Node.js let me show you how we can refactor the example in the post: RESTful Example using ExpressJS + Node.js + MongooseJS and modularize it.
Modularizing RESTful Example : Node.js Application
As stated at the beginning there would be a module for DB related code, a module for controller operations and then an application starting point. The directory structure of the sample looks like below:
Install the required packages i.e express, body-parser, mongoose using the command npm install express body-parser mongoose --save
.
The code for the book_dao.js is as given below:
//Filename: book_dao.js //mongoose is used for interacting with MongoDB var mongoose = require('mongoose'); 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; module.exports.findOne = function(isbn, callback){ Book.findOne({isbn: isbn}, function(err, result){ if ( err ) throw err; callback(result); }); } module.exports.findAll = function(callback){ Book.find({}, function(err, result){ if ( err ) throw err; callback(result); }); } module.exports.addNewBook = function(body, callback){ var book = new Book({ name:body.name, isbn: body.isbn, author: body.author, pages: body.pages }); //Saving the model instance to the DB book.save(function(err, result){ if ( err ) throw err; callback({ messaage:"Successfully added book", book:result }); }); } module.exports.editBook = function(body, isbn, callback){ Book.findOne({isbn: isbn}, function(err, result){ if ( err ) throw err; if(!result){ callback({ message:"Book with ISBN: " + isbn+" not found.", }); } result.name = body.name; result.isbn = body.isbn; result.author = body.author; result.pages = body.pages; result.save(function(err, result){ if ( err ) throw err; callback({ message:"Successfully updated the book", book: result }); }); }); } module.exports.deleteBook = function(isbn, callback){ Book.findOneAndRemove({isbn: isbn}, function(err, result){ callback({ message: "Successfully deleted the book", book: result }); }); }
From the above code you can see that the book_dao.js
depends only on the Mongoose API.
The code for book_controller.js
is as given below:
var bookDao = require("./book_dao"); module.exports.getBookDetails = function(params, callback){ console.log("Fetching details for book with ISBN: " + params.isbn); bookDao.findOne(params.isbn, callback); } module.exports.getAllBooks = function(callback){ console.log("Fetching all books"); bookDao.findAll(callback); } module.exports.addNewBook = function(body, callback){ console.log("Adding new book"); bookDao.addNewBook(body, callback); } module.exports.editBook = function(body, isbn, callback){ console.log("Editing Book"); bookDao.editBook(body, isbn, callback); } module.exports.deleteBook = function(isbn, callback){ console.log("Deleting book"); bookDao.deleteBook(isbn, callback); }
The code for routes.js
is given below:
var bookController = require("./book_controller"); module.exports = function(app){ //Get the details of the book with the given isbn app.get('/book/:isbn', function(req, res){ bookController.getBookDetails(req.params, function(results){res.json(results);}); }); //Get all the books app.get('/book', function(req, res){ bookController.getAllBooks(function(results){res.json(results);}); }); app.post('/book', function(req, res){ bookController.addNewBook(req.body, function(results){ res.json(results); }); }); app.put('/book/:isbn', function(req, res){ bookController.editBook(req.body, req.params.isbn, function(results){ res.json(results); }); }); app.delete('/book/:isbn', function(req, res){ bookController.deleteBook(req.params.isbn, function(results){ res.json(results); }); }); }
routes.js
is responsible for mapping the URLs to the methods in the controller.
From above code its clear that book_dao.js depends only on Mongoose, book_controller.js depends only on the book_dao.js and routes.js depends only on book_controller.js
Running Modular Node.js Example Application
The below is the code for app.js
which is the starting point for our application or in other words the place where the server is initiated and launched.
//Filename: app.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'); 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()); //Including the routes module var routes = require("./lib/routes"); routes(app); //Starting up the server on the port: 3300 app.listen(app.get('port'), function(){ console.log('Server up: http://localhost:' + app.get('port')); });
The above app.js depends on routes.js to setup the routes and controller binding.
Lets run the above sample as shown below:
G:\node\testapp\modular_sample_2>node app.js Server up: http://localhost:3300
The code for this sample can be found in the github as well.
I hope this tutorial helped you to understand how to modularize a Node.js application. This tutorial used RESTful example from our previous tutorials. Note that there is no one rule for modularizing the code, it can be improvized based on your experience. If you have any questions on writing the modular Node.js applications, please write it in the comments section.