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.
- Understand the basics of CRUD and REST
- Create RESTful endpoints which perform all of the CRUD operations
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.
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"
topackage.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}]
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.js
and 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.
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.
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.
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 therequest.body
, so you should check that they match as well.
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.