Hello and welcome to part 3 of my tutorial Building a REST API with MongoDB, Node and Express. If you haven't already please check out part 2. In this part we will finish our user API both the model and the routes

Disclaimer: Every effort has been made to make sure the code in these tutorials are correct. But sadly mistakes happen! If you notice any please let me know via my social media!

As always the code is available from this repository: http://gogs.dev.dylanscott.me/dylanrhysscott/series-rest-api

Stubbing our methods and routes

  1. Stub out functions in models/user.js for getting users, get a user, updating a user, and deleting a user like this...Notice that the single methods like getUser and updateUser accept a user ID. This will allow us to select the correct record to update
  getUsers: (e) => {

  },

  getUser: (e, userId) => {

  },

  updateUser: (e, userId, user) => {

  },

  deleteUser: (e, userId) => {

  }
  1. Stub out the rest of our routes in routes/users.js like this. Notice they are following RESTful patterns. For example PUTing to /:id will update the user with the passed ID. You may see as part of certain URL's we are defining a : then a parameter. In this case ID. In Express terms this denotes a parameter which can be passed into the URL. This gives us some flexibility and allows us to pass data in our requests without a querystring!
router.get('/', (req, res, next) => {

});

router.get('/:id', (req, res, next) => {

});

router.put('/:id', (req, res, next) => {

});

router.delete('/:id', (req, res, next) => {

});

 Filling it all in!

Pre warning - Due to the way the Express framework works a lot of this code may appear repetitive! Pay attention to the small changes as they can make a big difference!

Starting with the model

  1. For getUsers - Here we are following the same format as our create method but rather than creating a new instance of the model we are running a find() against it. It contains the query {} which represents everything. The rest you're familiar with.
const promise = new Promise((resolve, reject) => {  
      UserModel.find({}, (err, results) => {
        if(err) {
          reject(err);
        }

        if(results) {
          resolve(results);
        }
      });
    });

    promise.then((users) => {
      e.emit('getUsers', null, users);
    })
    .catch((err) => {
      e.emit('getUsers', err, null);
    });
  1. For getUser - The method is nearly identical to getUsers except we are querying by ID through the query {_id: userId}. This contains the ID passed through from the route request which we will write.
const promise = new Promise((resolve, reject) => {  
      UserModel.find({_id: userId}, (err, result) => {
        if(err) {
          reject(err);
        }

        if(result) {
          resolve(result);
        }
      });
    });

    promise.then((user) => {
      e.emit('getUser', null, user);
    })
    .catch((err) => {
      e.emit('getUser', err, null);
    });
  1. For the updateUser method - We are calling the update method on our model. Just like the query for a single user we are passing in the ID of the user we want to update. In addition to our query modifier there is another object which is calling the $set with the new data from the request. MongoDB will take the new data and apply it to any keys in the user document, overwriting the old data.
 const promise = new Promise((resolve, reject) => {
      UserModel.update({_id: userId}, {$set: user}, (err, result) => {
        if(err) {
          reject(err);
        }

        if(result) {
          resolve(result);
        }
      });
    });

    promise.then((user) => {
      e.emit('updateUser', null, user);
    })
    .catch((err) => {
      e.emit('updateUser', err, null);
    });
  1. For the deleteUser method - We are calling the remove() method on the model and passing the ID of the user we want to remove.
deleteUser: (e, userId) => {  
    const promise = new Promise((resolve, reject) => {
      UserModel.remove({_id: userId}, (err, result) => {
        if(err) {
          reject(err);
        }

        if(result) {
          resolve(result);
        }
      });
    });

    promise.then((user) => {
      e.emit('deleteUser', null, user);
    })
    .catch((err) => {
      e.emit('deleteUser', err, null);
    });

As you can see Mongoose makes our database interactions very intuative! Combining this with Async Promises leads to a very powerful was to complete non blocking database operations.

The API routes

  1. For the GET request to / - we will create a new instance of our event emitters, define our event we want to listen to which contains how we want to respond in the case of an error or results. Finally we call our method getUsers() passing in the event instance
var UserEvents = new EventEmitter();  
  UserEvents.once('getUsers', (err, users) => {
    if(err) {
      res.status(500).json({message: 'Could not retrieve users', err: err});
    }

    if(users) {
      res.status(200).json(users);
    }
  });

  UserModel.getUsers(UserEvents);
  1. For the GET request to /:id - Again it is near identical to the GET request to / but this time we are retrieving the ID from the URL with var id = req.params.id;. We call the method getUser() passing in our events instance and the ID.
var UserEvents = new EventEmitter();  
  var id = req.params.id;
  UserEvents.once('getUser', (err, user) => {
    if(err) {
      res.status(500).json({message: 'Could not retrieve user', err: err});
    }

    if(user) {
      res.status(200).json(user);
    }
  });

  UserModel.getUser(UserEvents, id);
  1. For the PUT request to /:id - We are still retrieving the ID but also getting the request body of data to update the user with. The rest you're familiar with...
var UserEvents = new EventEmitter();  
  var id = req.params.id;
  var data = req.body;
  UserEvents.once('updateUser', (err, user) => {
    if(err) {
      res.status(500).json({message: 'Could not update user', err: err});
    }

    if(user) {
      res.status(200).json(user);
    }
  });

  UserModel.updateUser(UserEvents, id, data);
  1. Finally for the DELETE request to /:id we are building a similar request to get a single user. Here it is for completeness:
var UserEvents = new EventEmitter();  
  var id = req.params.id;
  UserEvents.once('deleteUser', (err, users) => {
    if(err) {
      res.status(500).json({message: 'Could not delete user', err: err});
    }

    if(users) {
      res.status(204).json({});
    }
  });

  UserModel.deleteUser(UserEvents, id);

A note on HTTP status codes

Predominately in this tutorial we have either used a 200 or a 500 to signify to the browser what happened during our request. This is technically not all we should do when building an API. We should ensure that we are using the correct status codes for our operations. Check out this link for reference http://www.restapitutorial.com/httpstatuscodes.html if you are in doubt about the REST spec.

Testing it all out

That's it on the code front - fire up / restart your app if you haven't already done so as well as Postman. We have some testing to do! See the screenshots below for each endpoint test and it's results. Replicate them to ensure everything is working! Be sure to change any ID's in Postman to the ones for your application!

  1. Getting all users

get-all-users

  1. Getting a single user

get-single-user

  1. Updating a user

updating-single-user

  1. Deleting a user

delete-single-user

Wrapping things up...

Congratulations! You have just written your first full CRUD RESTful compliant API. You've now learnt all the principles to apply this to a different model and set of routes. Next lesson we will take a look at the Posts section of the API. I will provide larger code samples in the interest of time without as in depth an explanation. If you need a refresher on topics check back to this tutorial.

Has this tutorial been useful? - Please give me feedback via my twitter handle @dylanrhysscott!