Skip to content

Instantly share code, notes, and snippets.

@cklanac
Created November 16, 2016 22:25
Show Gist options
  • Save cklanac/9b027866a1fd63c05e8be25b9180b271 to your computer and use it in GitHub Desktop.
Save cklanac/9b027866a1fd63c05e8be25b9180b271 to your computer and use it in GitHub Desktop.
Creating a Shopping List API

Creating a Shopping List API

In this lesson you will be using your Node.js and Express skills to create the back end for a simple Shopping List application. The application will allow users to add, read, update and remove shopping items. You'll learn about the CRUD model and RESTful APIs, then put this into practice when creating the back end.

Goals

  • Understand the basics of CRUD and REST
  • Create RESTful endpoints which perform all of the CRUD operations

Intro to REST and CRUD

To build your shopping list application you are going to be creating a RESTful API. REST (REpresentation State Transfer) is a style of software architecture, a set of principles for designing APIs.

REST constrains your API to only communicate the state of resources (such as the items of our shopping list). The most generic operations that you use to modify the state of any object are the CRUD operations: Create, Read, Update, and Delete. You will be using these operations to add, read, update, and remove our shopping items.

The REST pattern also constrains your URL design to make sure that you only deal with resources. Even though you have to add an item, the url shouldn't be /item/add or /add-item. Notice how these URL include both the noun (item), and the verb (add). Instead your URL should only include a noun: /item. The verb part of the URL is communicated in the HTTP method: GET, POST, PUT, or DELETE. In this lesson, you will see how these methods map to the CRUD operations, providing you with a simple interface.

Jump into the next assignment to see how to take these ideas and put them into practice by building a RESTful API.

GETing a list of items

Now let's get working on building the shopping list app. To start, you are going to build the functionality that allows users to view the items in your shopping list.

The first thing to do is come up with an URL. To obey REST constraints, we choose /items. This URL is noun centric and easily understandable. It will also be the basis for all the URL's that deal with your items. When you're creating an API, the routes are referred to as endpoints. Your first endpoint will be for the HTTP GET method. By specifying that the HTTP method must be GET, it will be clear to your API users that this endpoint retrieves the item list. Here is what our HTTP request line should look like:

  • GET /items

Now that you know how this request is represented in HTTP, let's set up the project, and create a server that will process this request.

  • Create a new project in HyperDev

  • Replace the contents of index.html with this gist

    • This is the client side code for the app
  • Add "body-parser": "^1.15.2" to package.json. It should look something like this:

	"dependencies": {
    "express": "^4.12.4",
    "body-parser": "^1.15.2"
	},
  • Add the following code to your server.js file:
// server.js
// where your node app starts

// init project
const express = require('express');
const app = express();

// we've started you off with Express, 
// but feel free to use whatever libs or frameworks you'd like through `package.json`.

// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));

// http://expressjs.com/en/starter/basic-routing.html
app.get("/", function (request, response) {
  response.sendFile(__dirname + '/views/index.html');
});

app.get('/items', function(request, response) {
    response.json(storage.items);
});

// POST goes here

// Simple in-memory store for now
var Storage = {
  add: function(name) {
    var item = {name: name, id: this.setId};
    this.items.push(item);
    this.setId += 1;
    return item;
  } 
};

var createStorage = function() {
  var storage = Object.create(Storage);
  storage.items = [];
  storage.setId = 1;
  return storage;
}

var storage = createStorage();

storage.add('Broad beans');
storage.add('Tomatoes');
storage.add('Peppers');

// listen for requests :)
var listener = app.listen(process.env.PORT, function () {
  console.log('Your app is listening on port ' + listener.address().port);
});

Here you include the Express module. Then, you setup the building blocks of a storage object that is going to act like your shopping list database. You first create an add method for your Storage prototype, so that .add() will be available on all future instances -- later, expect to write your own methods for other actions like edit or remove. Then you make a factory function createStorage which you can use to create any number of objects that inherit from Storage. These objects start their life with an empty items array and a record of the latest unique id (setId) for each item. Finally, you add some "mock" data to your storage object in the form of three shopping list items.

Next you create the app object and then tell it to use the express.static middleware. This tells express to serve any static content contained in the public folder. When a request is made to the server for a static file (like, your CSS file), Express will look for it in the directory you specify. Also, if your server doesn't have a route that handles the request, it will look for a corresponding HTML file in that folder. Notice that the code above has no root (/) route. When the browser requests the root, Express will look for index.html in the public directory.

You then have a single route for get requests to the /items URL. In the route you return the storage.items list as JSON. Finally you tell the app to listen for requests on the configured IP.

  • Click the "Show" button to view the starter shopping list
  • Make sure that this is working with the front end by previewing the root of your app. You should see your initial shopping list data listed in the app.
  • Next, add /items to the URL. You should see something like this:
[{"name":"Broad beans", "id": 1},{"name":"Tomatoes", "id": 2},{"name":"Peppers", "id": 3}]

POSTing new shopping items

Now that you have an endpoint to retrieve the items, let's create the endpoint to add items. When trying to come up with a URL for adding an item, it might be tempting to use /item/add, but we have to remember our REST constraints, and restrict ourselves to nouns, so the URL will stay /items. To create new items, the verb POST is used in REST. So here is what our HTTP request line looks like:

  • POST /items

Along with the request line, an HTTP message includes headers and a body. The body is the standard place to put information that gets transmitted to the server. In the previous exercise, the items in the list were formatted using JSON, so let's continue to use the same format to represent an item: { "name": "Salsa" }. Below is what a sample request would look like.

POST /items HTTP/1.1
Content-Type: application/json

{ "name": "Durian" }

Note how the request includes the header explaining how the data in body is encoded, and it includes the body itself after an empty line.

Now that you know how this request is represented in HTTP, let's figure out how to process this request. First you need a way to access the body of the post request. This is where the body-parser module comes in. body-parser gathers the body data as it is streamed from the client, and then parses it according to its data type.

Let's see this in action. In your server.js file from the previous assignment, require the body-parser module, and create a JSON parser:

const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();

Next find // POST goes here around line 23 in server.jsand paste in this endpoint:

// Uses POST body: http://expressjs.com/en/api.html#req.body
app.post('/items', jsonParser, function(request, response) {
    if (!('name' in request.body)) {
        return response.sendStatus(400);
    }

    var item = storage.add(request.body.name);
    response.status(201).json(item);
});

Notice how the second argument to the post method is jsonParser. This tells express to use the jsonParser middleware when requests for the route. The middleware adds a new attribute, request.body, to the request object. If the name property is missing from the request body you use response.sendStatus to indicate a 400 Bad Request.

If the body contains the item name, then you simply add the item to the shopping list, and return a 201 Created status, along with the item.

Save your code and run the server, then let's test the code. Preview your app and try adding a new item to the list. You should see that it has been appended to the shopping list.

PUTing and DELETEing items

In the previous assignments you created a server that allows you to add items to your shopping list and view those items. Now you need a way to delete the items from your list when you have successfully picked them up from the store, and edit them when you change your mind about buying an item.

Part 1 - adding a DELETE endpoint

To complete this section of the assignment you should create a DELETE endpoint for /items/<id>. For example, making a delete request to /items/3 would delete the item with ID 3.

Requirements

  • Create an endpoint that responds to a DELETE request to /items/:id
  • If the item is succesfully deleted, the server should respond with a 200 OK status
  • If an incorrect ID is supplied, your endpoint should fail gracefully, returning a 404 Not Found status and a JSON error message.

Part 2 - adding a PUT endpoint

To complete this section of the assignment you should create a PUT endpoint for /items/<id>. For example, sending the JSON object {"name": "Durian", "id": 3} to /items/3 should set the item with ID 3 to be a Durian.

Requirements

  • Create an endpoint that responds to a PUT request to /items/:id
  • If the item is edited, the server should respond with a 200 OK status and the new item
  • If the request is incorrectly formed (bad body, missing id), the request should fail gracefully
  • If a non-existent ID is supplied, your endpoint should create a new item using the ID supplied.
  • Remember that you're passing the ID in the request.params and the request.body, so you should check that they match as well.

Tip: Making requests using cURL

You will find that using a browser to debug your endpoints is very cumbersome. For example, you would need to use an AJAX request to send JSON data to your API. cURL is a command-line utility which you can use to make HTTP requests directly to your API. For example, to make requests to the four CRUD endpoints in this project you could run the following commands (while your server is running):

# Make a GET request to fetch the list of items
curl http://localhost:8080/items

# Make a POST request to add an item
curl -X POST -H "Content-Type: application/json" -d '{"name": "durian"}' http://localhost:8080/items

# Make a PUT request to edit an item
curl -X PUT -H "Content-Type: application/json" -d '{"name": "durian", "id": 3}' http://localhost:8080/items/3

# Make a DELETE request to delete an item
curl -X DELETE http://localhost:8080/items/3

The -X flag to cURL describes which HTTP method you are using (GET, POST, PUT or DELETE). The default is to make a GET request. The -H flag allows you to provide additional headers for the request. Notice how in the POST and PUT requests you add a header saying that the content of the request body will be JSON. The -d flag is used to provide the data which will be sent in the request body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment