Scott Smith

Blog Tutorials Projects Speaking RSS

Beer Locker: Building a RESTful API With Node - CRUD

Welcome to part 2 of the Beer Locker series

  1. Getting started
  2. CRUD
  3. Passport
  4. OAuth2 Server
  5. Digest
  6. Username & Password

In our previous article we left off with a basic Node applicaton in place capable of accepting HTTP requests and responding back with some static JSON.

In this part we will dive a bit deeper and learn how to implement CRUD operations on our beer locker. By the end of this article you will have learned how to connect to a MongoDB, used Mongoose for object modeling, and have implemented GET, PUT, POST, and DELETE endpoints.

Connecting to MongoDB

If you don’t already have MondgoDB installed and running, you will want to go their official site and follow their installation instructions.

There are three things we need to do to connect to our MongoDB.

  1. Install the Mongoose package
  2. Load the Mongoose package
  3. Connect to it useing our connection string

Install the package manually using the following command:

1
npm install mongoose --save

Update the code in server.js from our previous article to look like the following.

1
2
3
// Load required packages
var express = require('express');
var mongoose = require('mongoose');

Connect to the MongoDB

1
2
3
4
5
6
// Load required packages
var express = require('express');
var mongoose = require('mongoose');

// Connect to the beerlocker MongoDB
mongoose.connect('mongodb://localhost:27017/beerlocker');

If all goes well, your application should start up just fine.

Create our first model - BEER

Go ahead and create the JavaScript file that will hold our beer model.

1
2
3
beerlocker/
  models/         // holds our models
    beer.js

Add the following code to the newly created file.

1
2
3
4
5
6
7
8
9
10
11
12
// Load required packages
var mongoose = require('mongoose');

// Define our beer schema
var BeerSchema   = new mongoose.Schema({
  name: String,
  type: String,
  quantity: Number
});

// Export the Mongoose model
module.exports = mongoose.model('Beer', BeerSchema);

So what is going on here?

  1. We loaded the Mongoose package
  2. Created a Mongoose schema which maps to a MongoDB collection and defines the shape of the documents within that collection.
  3. We defined our schema to contain 2 strings and 1 number.
  4. We exported the Mongoose beer model for use within our application.

The last step is to load this new beer model in our server.js file.

1
2
3
4
// Load required packages
var express = require('express');
var mongoose = require('mongoose');
var Beer = require('./models/beer');

Get ready to accept data via POST or PUT

In order to accept data via POST or PUT, we need to add another package called body-parser.

1
npm install body-parser --save

Load the body-parser package in our code.

1
2
3
4
5
// Load required packages
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var Beer = require('./models/beer');

And finally, we need to use this package with our Express application.

1
2
3
4
5
6
7
// Create our Express application
var app = express();

// Use the body-parser package in our application
app.use(bodyParser.urlencoded({
  extended: true
}));

Add some beer to our beer locker

The moment we have all been waiting for is here. We are now going to implement the means to add beer to our locker!

Let’s add this code to our server.js file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Initial dummy route for testing
// http://localhost:3000/api
router.get('/', function(req, res) {
  res.json({ message: 'You are running dangerously low on beer!' });
});

// -- New Code Below Here -- //

// Create a new route with the prefix /beers
var beersRoute = router.route('/beers');

// Create endpoint /api/beers for POSTS
beersRoute.post(function(req, res) {
  // Create a new instance of the Beer model
  var beer = new Beer();

  // Set the beer properties that came from the POST data
  beer.name = req.body.name;
  beer.type = req.body.type;
  beer.quantity = req.body.quantity;

  // Save the beer and check for errors
  beer.save(function(err) {
    if (err)
      res.send(err);

    res.json({ message: 'Beer added to the locker!', data: beer });
  });
});

What we are doing here is creating a new route with the prefix ‘/beers’ and then setting up what to do when we POST to that endpoint. In this case we create a new Beer model, set its properties to those passed in as data in the POST, and call save on the Beer model which is a Mongoose function that will save the model to the MongoDB database.

Fire up Postman and test is out. Make sure to switch the data type to x-www-form-urlencoded. You will want to add 3 key value pairs for name, type, and quantity, choose POST as the method, and enter the URL http://localhost:3000/api/beers.

Postman

Party time, let’s get all the beer!

Getting all our beer is pretty simple. We implement it in a similar fashion as we did for POST. Add the following code to server.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Create endpoint /api/beers for POSTS
beersRoute.post(function(req, res) {
  ...
});

// -- New Code Below Here -- //

// Create endpoint /api/beers for GET
beersRoute.get(function(req, res) {
  // Use the Beer model to find all beer
  Beer.find(function(err, beers) {
    if (err)
      res.send(err);

    res.json(beers);
  });
});

What we are doing here is creating a new route to the prefix ‘/beers’ and then setting up what to do when we make a GET request to that endpoint. In this case we use the Mongoose Beer model to call find which will query the MongoDB database and return all our beer.

Fire up Postman and test it out by making a GET request to http://localhost:3000/api/beers. If everything is working fine, you should get back all the beer you have added.

Pace yourself with a single beer

We really don’t want to drink all of our beer at once, so we need a way to get out a single beer.

Time to update server.js again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Create endpoint /api/beers for GET
beersRoute.get(function(req, res) {
  ...
});

// -- New Code Below Here -- //

// Create a new route with the /beers/:beer_id prefix
var beerRoute = router.route('/beers/:beer_id');

// Create endpoint /api/beers/:beer_id for GET
beerRoute.get(function(req, res) {
  // Use the Beer model to find a specific beer
  Beer.findById(req.params.beer_id, function(err, beer) {
    if (err)
      res.send(err);

    res.json(beer);
  });
});

Because we are wanting to request a single beer, we needed to implement a new route. This new route contains the id of the beer we want /api/beers/:beer_id'. With this new route, we then setup what to do when it is called with a GET. We end up using the Mongoose Beer model function findById() and pass in the beer_id parameter to look up the requested beer.

To test this out, make a request to your API and get out all of your beer. Pick out an id from one that you want to request individually. Finally using Postman you can make a request to http://localhost:3000/api/beers/:beer_id and replace :beer_id with your id.

Postman

Updating the quantity for beers we just drank

We are now pulling beers out of our locker. For each beer we remove, we need to update our quantity so we know how many we have left. This can be done by implementing support for the PUT method.

You guessed it, let’s update the server.js file again.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Create endpoint /api/beers/:beer_id for GET
beerRoute.get(function(req, res) {
  ...
});

// -- New Code Below Here -- //

// Create endpoint /api/beers/:beer_id for PUT
beerRoute.put(function(req, res) {
  // Use the Beer model to find a specific beer
  Beer.findById(req.params.beer_id, function(err, beer) {
    if (err)
      res.send(err);

    // Update the existing beer quantity
    beer.quantity = req.body.quantity;

    // Save the beer and check for errors
    beer.save(function(err) {
      if (err)
        res.send(err);

      res.json(beer);
    });
  });
});

Just like we did for getting a single beer, we used the same route but implemented functionality to handle PUT requests. We lookup the beer the same way, update its quantity, and then save it back to MongoDB.

Using the same URL you used to GET a single beer, update Postman to use PUT, set data type to x-www-form-urlencoded, and add a key value pair quantity set to whatever number you want.

You should get back a response with the beer object’s quantity updated.

Postman

I just drank my last beer!

Sometimes, we drink the last beer. In this case, updating the quantity to 0 isn’t appropriate. What we need is the ability to delete the beer entirely.

This can be accomplished by implementing functionality to support DELETE requests to our endpoint we used for GET and PUT.

Update server.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Create endpoint /api/beers/:beer_id for PUT
beerRoute.put(function(req, res) {
  ...
});

// -- New Code Below Here -- //

// Create endpoint /api/beers/:beer_id for DELETE
beerRoute.delete(function(req, res) {
  // Use the Beer model to find a specific beer and remove it
  Beer.findByIdAndRemove(req.params.beer_id, function(err) {
    if (err)
      res.send(err);

    res.json({ message: 'Beer removed from the locker!' });
  });
});

Just like our PUT, DELETE uses the id passed in. We then use the Mongoose findByIdAndRemove function to find and delete our object. Update Postman to use DELETE instead of PUT and send the request.

Postman

You should now be able to switch the method to GET and receive and error since the object with that id no longer exists.

Wrap up

We have now implemented full CRUD on our Beer Locker. Up next will be diving into creating user accounts and authenticaion using Passport.

Source code for this part can be found here on GitHub.