Software Development

Writing RESTful Service using Node, Express and MongoDB

Looking at the popularity of Node.js, I thought of learning and creating a rest api that should create, delete and get employees from database and fortunately I did it.

With this post, I will try to replicate the steps I followed while writing it along with references.

Assuming Node.js is already installed in our system, let’s start creating nodejs-rest-api, following below steps:

Step 1: Create a directory which will contain all the files related to our app, for me it’s /Users/ArpitAggarwal/nodejs-rest-api and execute npm init:

$ cd /Users/ArpitAggarwal/
$ mkdir nodejs-rest-api
$ npm init

npm init command specified above will create package.json which helps us to manage dependencies.

Step 2: Install Express as a dependency:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ npm install --save express

Step 3: Create default entry point for Node.js i.e server.js, inside the same directory we created earlier:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ touch server.js

Copy below content in server.js:

const express = require('express');
const app = express();

app.listen(9999, function() {
  console.log('Node server listening on 9999')
})

app.use(express.static(__dirname + '/public'));

Let’s understand what does each line mean.

require – imports modules into the current file.
const express = require(‘express’) – Creates an Express application.
const app = express() – Is app object conventionally denotes the Express application.
app.listen(9999) – Starts a UNIX socket and listens for connections on the given path.
app.use(express.static(__dirname + ‘/public’)) – Express serve static content for the app from the “public” directory in the application directory.

Step 4: Create index.html inside directory /Users/ArpitAggarwal/nodejs-rest-api/public as follows:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ mkdir public
$ cd public
$ touch index.html

Copy below content in index.html:

Hello Node!

Step 5: Modify server.js to add GET mapping to display the index.html we just created:

app.get('/', function(req, res){
  res.sendFile('/index.html')
});

Step 6: Move to directory /Users/ArpitAggarwal/nodejs-rest-api and start the app following below command:

$ cd /Users/ArpitAggarwal/nodejs-rest-api
$ node server.js

Now, opening http://localhost:9999/ in browser or executing command:

curl http://localhost:9999

will show us “Hello Node!” as response.

Step 7: Now, we will install MongoDB as a dependency moving to the directory /Users/ArpitAggarwal/nodejs-rest-api/:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ npm install --save mongodb

Step 8: Create config.js which will store all the configurable parameters of the app like username, password, urls, etc. It basically helps us to specify the input to an app at runtime based on different environments on which we are going to run the app:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ touch config.js

Copy the below content in config.js:

var config = {};

config.mongodb = {};
config.server = {};

config.server.port = process.env.WEB_PORT || 9999;

config.mongodb.username = process.env.MONGODB_USERNAME || 'arpit';
config.mongodb.password= process.env.MONGODB_PASSWORD || 'xxxx';
config.mongodb.host= process.env.MONGODB_HOST || 'ds047752.mlab.com';
config.mongodb.port = process.env.MONGODB_PORT || 47752;
config.mongodb.databaseName = process.env.MONGODB_NAME || 'my-database';

module.exports = config;

Do replace the properties with your’s or export from command line before starting the app, for example:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ export MONGODB_PASSWORD = p@ssword
$ node server.js

Step 9: Create mongodb.js where we will define MongoClient and export the connection to database as well as database object to utilize in a non redundant fashion while defining at one place, as follows:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ touch mongodb.js

Copy the below content in mongodb.js:

var MongoClient = require( 'mongodb' ).MongoClient;
var util = require('util');
var config = require('./config');
var _db;

var uri = util.format('mongodb://%s:%s@%s:%d/%s',
    config.mongodb.username, config.mongodb.password, config.mongodb.host, config.mongodb.port, config.mongodb.databaseName);

module.exports = {
  connectToServer: function( callback ) {
    /** Connect to the Mongo database at the URI using the client **/
    MongoClient.connect( uri, { auto_reconnect: true }, function( err, db ) {
      if (err) throw err;
      else if (!db) console.log('Unknown error connecting to database');
      else {
        console.log('Connected to MongoDB database server at:');
        console.log('\n\t%s\n', uri);
        _db = db;
      }
      return callback( err );
    } );
  },
  getDb: function() {
    return _db;
  }
};

require( ‘mongodb’ ).MongoClient specified above create a new MongoClient instance.

var config = require(‘./config’) – Imports config.js into the current file.

Step 10: Now let’s modify server.js to use the above two files we just created and setting up the server to listen on specified port only when MongoDB connection is successful:

const mongodb = require('./mongodb.js');
const config = require('./config.js');
var db

mongodb.connectToServer( function( err ) {
  app.listen(config.server.port, function() {
     console.log('Node server listening on ' + config.server.port);
     db = mongodb.getDb();
  })
});

Step 11: Next, modify GET request in server.js to get the list of employees from database using the MongoClient we just created, as follows:

app.get('/employee/get', function(req, res){
  db.collection('employees').find().toArray(function(err, results) {
  res.send(results);
  })
  res.set({
    'Cache-Control': 'no-cache'
  });
});

Executing the below command will list all the employees we create:

$ curl http://localhost:9999/employee/get

Step 12: Moving further to extend our app to create employee in a database by adding POST request. But, before introducing POST request we have to install body-parser as a node dependency which will help in parsing incoming request bodies in a middleware, as follows:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ npm install --save body-parser

Same like other dependencies we have to import it using require keyword to leverage the capability for parsing the JSON request, as follows:

const bodyParser= require('body-parser')

app.use(bodyParser.json())

app.post('/employee/create', (req, res) => {
  db.collection('employees').save(req.body, (err, result) => {
    if (err) return console.log(err)
    res.send('Employee created!');
  })
})

Now everything is in place for POST, let’s create an employee:

$ curl -H "Content-Type: application/json" -X POST -d '{"name": "Arpit Aggarwal","email":"aggarwalarpit.89@gmail.com"}' http://localhost:9999/employee/create

Next, we will extend our app to delete an employee and to that we will define the DELETE mapping in server.js, as follows:

app.delete('/employee/delete', (req, res) => {
  db.collection('employees').findOneAndDelete({name: req.body.name},
  (err, result) => {
    if (err) return res.send(500, err)
    res.send('Employee deleted!')
  })
})

Let’s verify DELETE call:

curl -H "Content-Type: application/json" -X DELETE -d '{"name": "Arpit Aggarwal"}' http://localhost:9999/employee/delete

Tired of restarting the Node.js server everytime you make changes to app?

If yes, we can use Nodemon, a utility that will monitor for any changes in our source and automatically restart our server. Let’s install it globally:

$ npm install -g nodemon

Once installed, we have to start our app with nodemon instead of node, for example:

$ cd /Users/ArpitAggarwal/nodejs-rest-api/
$ nodemon server.js

Complete source code is hosted on github.

Arpit Aggarwal

Arpit is a Consultant at Xebia India. He has been designing and building J2EE applications since more than 6 years. He is fond of Object Oriented and lover of Functional programming. You can read more of his writings at aggarwalarpit.wordpress.com
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button