• No results found

Using the PUT book AP

In document Node js the Right Way {TechieAce} pdf (Page 113-116)

Before we get into the code, let’s take a look at how this API works. That way, when we dive into the code it’ll be more clear what it’s doing.

To use the PUT book API, we need a bundle, so make one with the POST API through curl:

$ curl -X POST http://localhost:3000/api/bundle?name=War%20Books {

"ok": true,

"id": "d476de5d13555f2e8dd0eaac7c000984", "rev": "1-d90a9d2a08880d9f29e6fa4515702d50" }

Request the bundle with GET to see what’s in it so far (your bundle ID may be different): $ curl http://localhost:3000/api/bundle/d476de5d13555f2e8dd0eaac7c000984 { "_id": "d476de5d13555f2e8dd0eaac7c000984", "_rev": "1-d90a9d2a08880d9f29e6fa4515702d50", "type": "bundle",

"name": "War Books", "books": {}

}

Then, let’s add Sunzi’s The Art of War (Project Gutenberg ID 132):

$ curl -X PUT \ http://localhost:3000/api/bundle/d476de5d13555f2e8dd0eaac7c000984/book/132 { "ok": true, "id": "d476de5d13555f2e8dd0eaac7c000984", "rev": "2-537b36e10212810fa3d081a9afc64282" }

Finally, let’s see what was added to the bundle:

$ curl http://localhost:3000/api/bundle/d476de5d13555f2e8dd0eaac7c000984 {

"_id": "d476de5d13555f2e8dd0eaac7c000984", "_rev": "2-537b36e10212810fa3d081a9afc64282", "type": "bundle",

"name": "War Books", "books": {

"132": "The Art of War" }

}

Success! The “War Books” bundle now contains The Art of War.

Let’s take a look at the PUT handler that made this happen. This will be the last code block we inspect in the chapter, but it’s also the most complex.

Yielding Promises with Q.async

Here’s the section of lib/bundle.js that implements adding a book to a bundle with PUT:

web-services/b4/lib/bundle.js

Line 1

let

app.put('/api/bundle/:id/book/:pgid', function(req, res) {

- - get = Q.denodeify(request.get), - put = Q.denodeify(request.put); 5 - - Q.async(function* (){

let args, couchRes, bundle, book;

- -

// grab the bundle from the b4 database 10

args = yield get(config.b4db + req.params.id);

- couchRes = args[0]; - bundle = JSON.parse(args[1]); - -

// fail fast if we couldn't retrieve the bundle 15 if (couchRes.statusCode !== 200) { - res.json(couchRes.statusCode, bundle); - return; - } - 20

// look up the book by its Project Gutenberg ID -

args = yield get(config.bookdb + req.params.pgid);

- couchRes = args[0]; - book = JSON.parse(args[1]); - 25

// fail fast if we couldn't retrieve the book - if (couchRes.statusCode !== 200) { - res.json(couchRes.statusCode, book); - return; - } 30 -

// add the book to the bundle and put it back in CouchDB -

bundle.books[book._id] = book.title;

-

args = yield put({url: config.b4db + bundle._id, json: bundle});

- res.json(args[0].statusCode, args[1]); 35 - - })() - .catch(function(err) {

res.json(502, { error: "bad_gateway", reason: err.code });

- 40

}); });

The first thing to notice is line 4, where we call Q.denodeify() for request.get and

request.put. The denodeify() method takes a Node.js-style function (one that expects a callback) and returns a new promise-producing function from it. Using denodeify is a convenient way to avoid calling Q.nfcall() all over the place. Instead, you just denodeify the functions you plan to use, and call them later knowing they’ll produce promises.

Right after that, in line 7, we call Q.async() with a generator function. Q’s async

method returns a new promise-producing function that will start running the generator when you invoke it. We invoke this function right away on line 37. Inside the generator, any time we yield a promise, Q will wait for the promise to resolve, then resume execution with the resolved value. For example, on line 11, we yield a promise for a get call to CouchDB. When the request finish- es, the promise is resolved and Q gives us back the value, which we assign to args. This is much like the calls to then(function(args){...}) we saw earlier, but written in a more linear style.

If the HTTP status code from CouchDB was something other than the 200 OK we were hoping for, we send it back to the requester verbatim and return early. Otherwise, we move on to the second asynchronous call in line 22. Again we yield a promise for a get call to CouchDB, and again we check that everything was OK when it comes back.

Finally, on line 33, we add the book to the bundle and yield again—this time waiting on a put. When the put is finished, we send the results back to the requester. And if anything went wrong during all of this asynchronousness, we catch it on line 38.

This API makes three asynchronous requests, but does it in only one function thanks to Q.async and generator functions. On the plus side, writing code in this way has the potential to make it look like synchronous, linear code. But reaping this benefit means working deeply with promises and relying on some seemingly magical coordination logic.

Wrapping Up

This chapter took us on a crash course through writing RESTful APIs with Express. You learned how to create an Express-based web service and how to modularize your endpoints into separate files. You saw how to add script commands to npm and how to use nodemon for restarting your service as you make changes.

We also dug deep into promises, a novel way for managing asynchronous code. We used the Q library to create and chain promises, and handle error

cases where promises are revoked. You also saw how to convert Node.js-style functions—which expect a callback—into promise-generating functions. Finally, we used ECMAScript Harmony’s generator functions with Q to write synchronous-looking code that’s actually asynchronous. Although this code is easy to read, it masks some powerful logic that goes on behind the scenes. We saw how Express’s route methods (get(), post(), and friends) provide an easy way to set up path-based routing. And we used Express middleware to easily provide logging capabilities to all our routes.

You can do a whole lot more with Express and middleware than we covered in this chapter. In Chapter 7, Web Apps, on page 107, we’ll build on what we have here as we create a web-based client for using our book and bundle APIs.

In document Node js the Right Way {TechieAce} pdf (Page 113-116)

Related documents