Building a simple API using node.js

I have already introduced node.js in a previous article and explained how to install it in another one. Now it is time to go further with it. Basically what we will try to do today is to create a basic yet functional API server using node.js along with restify and mongoose, which are two modules.

First thing is, we have to understand what a module is. That is quite simple, a module is a packaged code that delivers a functionality using reachable (exported) functions. Restify, for instance, helps creating RESTful APIs easily, while mongoose is a MongoDB driver. Node.js provides a handful tool to deal with modules, like installing and removing, it is npm. so go ahead and install those two modules, type in terminal:

npm install restify mongoose

Yes, it is that easy! Now we have restify and mongoose installed, let's start coding. Create a new directory and step into it, create an app.js file and write the following

var restify = require('restify')
 , mongoose = require('mongoose');

these two lines actually load the modules, thanks to the require() function, and assign them to two different variables. Next step is to create a server

var server = restify.createServer({ name: 'mongo-api' })
 server.listen(7000, function () {
 console.log('%s listening at %s', server.name, server.url)
})

Go ahead and try it, type

node app.js

you will see something like

mongo-api listening at http://0.0.0.0:7000

This indicates that our server is up and running, good isn't it? But it is quite useless since we have not defined any routine yet. Before, we have to connect to MongoDB and define our model. For example we can make a user model having 3 properties:

  • Email
  • First Name
  • Last Name

Pretty simple and easy

db = mongoose.connect("mongodb://localhost/example-api");
var Schema = mongoose.Schema,
 ObjectId = Schema.ObjectID;
var User = new Schema({
 email: {
  type: String,
  required: true,
  trim: true
 },
 fname: {
  type: String,
  required: false,
  trim: true
 },
 lname: {
  type: String,
  required: false,
  trim: true
 },
});
var User = mongoose.model('User', User);

This should be enough to connect to the database and define our User model. There is an extra tweak to the server to enable the default headers for the system using fullResponse() and bodyParser() to remap the body content of a request to the req.params variable, allowing both GET and POST/PUT routes to use the same interface, fairly handy when you want to re-use code. Go ahead and apply them

server
 .use(restify.fullResponse())
 .use(restify.bodyParser())

We have now the server setup and ready, along with its database part. But we still need routines to define the behaviour of the API. Our first routine, is to grab all the records in the database when the user emits a GET request to /user

server.get('/user', function (req, res, next) {
 User.find({}, function (error, users) {
 res.send(users)
 })
})

To test this, we need to run the application and use curl to interact with it

curl -i http://localhost:7000/user

Since our database is still empty, this request should return something like [] indicating that nothing is found there. The next required routine is the post, where a request is able to add a record

server.post('/user', function (req, res, next) {
 if (req.params.email === undefined) {
  return next(new restify.InvalidArgumentError('Email must be supplied'))
 }
 var userData = {
  email: req.params.email,
  fname: req.params.fname,
  lname: req.params.lname
 }
 var user = new User(userData);
 user.save(function (error, data) {
  if (error) {
   return next(new restify.InvalidArgumentError(JSON.stringify(error.errors)))
  }
  else {
   res.json(data);
  }
  res.send(201, user)
 })
})

It is a bit more complicated than the first routine but it is not that hard. It just accepts the POST request map it into an object and save it into the database. We are doing a very basic verification step where we check for the email is already set in the request body. Let's test this!

curl -i -X POST -d '{ "email": "mail@jacer.info", "fname": "Jacer", "lname": "Omri" } ' http://localhost:7000/user

after successfully processing the request, the server would return this

{
 __v: 0
 email: "jokerhacker.jacer@gmail.com"
 fname: "Jacer"
 lname: "Omri"
 _id: "51e7bf1d84d3941816000001"
}

This means that the record has been added to the database, notice the _id key which is auto generated within MongoDB. Let's make another GET routine that only requests one record.

server.get('/user/:id', function (req, res, next) {
 User.findOne({ _id: req.params.id }, function (error, user) {
  if (error) return next(new restify.InvalidArgumentError(JSON.stringify(error.errors)))
  if (user) {
   res.send(user)
  } else {
   res.send(404)
  }
 })
})

This is quite similar to the first GET routine with one addition, it is the path /user/:id, which tells the application to wait for a value after /user/ and map it to req.params.id. In this case, it is the id of the record to retrieve . Let's see how it works

curl -i http://localhost:7000/user/51e7bf1d84d3941816000001

would return

{
 __v: 0
 email: "jokerhacker.jacer@gmail.com"
 fname: "Jacer"
 lname: "Omri"
 _id: "51e7bf1d84d3941816000001"
}

What is about updating an existing record?

server.put('/user/:id', function (req, res, next) {
 if (req.params.email === undefined) {
  return next(new restify.InvalidArgumentError('Email must be supplied'))
 }
 var userData = {
  email: req.params.email,
  fname: req.params.fname,
  lname: req.params.lname
 }
 User.update({ _id: req.params.id }, userData, {
  multi: false
 }, function (error, user) {
  if (error) return next(new restify.InvalidArgumentError(JSON.stringify(error.errors)))
  res.send()
 })
})

As you can see, this is the PUT routine which is similar to POST but it does not add a new record anyway, notice the update() function.  Let's say i want to change my e-mail address

curl -i -X PUT -d '{ "email": "jokerhacker.jacer@gmail.com", "fname": "Jacer", "lname": "Omri" } ' http://localhost:7000/user/51e7bf1d84d3941816000001

This way we tell the system to update the record having _id equals 51e7bf1d84d3941816000001 with the data provided in the request body, which will update the previous user with the new e-mail  address. Now the DELETE routine

server.del('/user/:id', function (req, res, next) {
 User.remove({ _id: req.params.id }, function (error, user) {
 if (error) return next(new restify.InvalidArgumentError(JSON.stringify(error.errors)))
res.send()
 })
})

This is relatively simple, it requires an id to be given in the request path so it would be deleted. Let's try it

curl -i -X DELETE http://localhost:7000/user/51e7bf1d84d3941816000001

To make sure the record is successfully deleted, just request the first GET request and you will not find this id.

This is it! We've already done building a basic API using Node.js and a minimum number of modules. You can go further with this, like creating security policies like login/pass or api keys. You can find a copy of the whole app.js file assembled here.

If you find this tutorial and others on this blog useful, follow me here and on social networks to keep in touch and read my coming articles.

blog comments powered by Disqus