/ javascript

Building a REST API with MongoDB, Node and Express - Part 2 - Building our user model and route

Hello and welcome to part 2 of my tutorial Building a REST API with MongoDB, Node and Express. If you haven't already please check out part 1. In this part we will get our database setup to connect on application start, build our posts model and start laying the groundwork for the routes of our API. Lets get started!

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

Configuring the Database

  1. Start the MongoDB server by running sudo mongod
  2. First we need to install the Mongoose driver - we can do this by running npm install mongoose --save. The ODM driver website can be found here http://mongoosejs.com/
  3. Next in bin/www add the line var mongoose = require('mongoose'); to the top of the file
  4. We can then define a connection string to connect to localhost:27017 and the database blog-api by defining the following block. As you can see we pass in a callback that accepts an error and we throw that error if there is one:
mongoose.connect('mongodb://localhost:27017/blog-api', (err) => {
  if(err) {
    throw new Error(err);
  } else {
   
  }
});
  1. Finally move the line server.listen(port); inside the else statement. This prevents the server from starting if there is no database connection. You can now start the server with the usual command DEBUG=blog-api:* npm start

Making the User Model

Assuming you have no errors we are now connected to the database and can make our model. Before we go ahead let me explain how Mongoose works: It works by defining an application level schema in Node which sits on top of MongoDB. Due to the way MongoDB works it has no schema which gives it a high degree of flexibility. By defining a schema it gives us more control over our data and provides methods with which we can interact with the database. So how do we define a model?

  1. Create a new file in your project called users.js under a folder called models/. Good structure and all that!
  2. We require the mongoose package and a method from that package called Schema. Then create a new instance of the Schema and pass a JSON object which represents the structure. Next we define the name for our model which is 'users' and pass in the newly created schema. Lastly you can see I have stubbed out our first method for our user model. This will be what we call to run the database operation. It accepts an Event Emitter and the user to save.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
  firstName: {
    type: String,
    required: true
  },
  lastName: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  dob: {
    type: Date,
    required: false
  }
});
const UserModel = mongoose.model('users', UserSchema);
module.exports = {
  createUser: (e, user) => {

  }
}

For now lets leave this to one side and create a route. The reasoning for this will all become clear in just a moment.

Creating the create user route

  1. Open the already defined file routes/users.js and add the following lines as well as defining our route:
var EventEmitter = require('events');
var UserModel = require('../models/user.js');

router.post('/', (req, res, next) => {
  var UserEvents = new EventEmitter();
  var user = req.body;
  console.log(user);
  UserEvents.once('createUser', (err, user) => {
    if(err) {
      res.status(500).json({message: 'Could not create user', err: err});
    }

    if(user) {
      res.status(200).json(user);
    }
  });
  UserModel.createUser(UserEvents, user);
});

We have required the events module to handle our events for us and our previously created User model. Next we define a post route on our router which creates an instance of event emitter and grabs the request body for the user information. We call the createUser method we made earlier passing in our data and define our event to listen for. This will either respond with a 500 or 200 depending on the result. The log statement is there for debugging purposes.

If you are unfamiliar with the Event Model pattern displayed here check out my tutorial http://dylanscott.me/understanding-the-nodejs-event-loop/

Wiring it all up

Now we have defined our route we can return to the createUser method on the model. I wanted to lay the ground work on both sides before filling out a function stub with no context. Basically we need to save the passed in data and return a response either way:

const promise = new Promise((resolve, reject) => {
      var userInstance = new UserModel(user);
      userInstance.save((err, result) => {
        if(err) {
          reject(err);
        }

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

    promise.then((result) => {
      e.emit('createUser', null, result);
    })
    .catch((err) => {
      e.emit('createUser', err, null);
    });
  1. This function creates a promise using the ES6 promise specification. The promise will be rejected or resolved with the relevant data
  2. If there is a result the promise will emit a null for the error and the actual result. The inverse will happened for errors. The Event Emitter will trigger the API to send a response one way or the other.

If you are not familiar with async promises check out my tutorial here http://dylanscott.me/async-programming-patterns-with-nodejs/

Testing it out

Believe it or not thats it! We can now restart the server and test it out! But how you ask!? There is no UI! Well that is just such a tool for the job called Postman. After you have set this up build the request as follows:

testing-the-request

You can see we choose the method (POST), and the body (our JSON object). Hit send and you'll see the results come back. This time an ID has been passed back which is the ID of the record in the database. Result! We can also see the console output of the body to verify the correct data is sent

request-results

 blog-api:server Listening on port 3000 +0ms
{ firstName: 'Dylan',
  lastName: 'Scott',
  dob: '2016-10-10',
  email: 'dylanrhysscott@gmail.com' }
(node:44101) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
POST /users 200 56.890 ms - 149

Wrapping things up...

Phew! We've covered a lot of ground today! In summary we have:

  • Connected to our database
  • Defined a model
  • Defined our first restful route
  • Written a method to save the data
  • Wired and tested everything out

In the next lesson we will flesh out our user's API even more and begin considering our other requirements!

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