• No results found

API Design

In document Learning Node js pdf (Page 159-163)

For the photo-sharing JSON server, you are developing what’s called a RESTful JSON server. The word REST comes from Representational State Transfer , and basically implies that you can request an accurate representation of an object from the server. REST APIs focus on four core operations (which, coincidentally, map to four HTTP request methods):

■ Creation ( PUT ) ■ Retrieval ( GET ) ■ Updating ( POST ) ■ Destruction ( DELETE )

Some people refer to these operations as CRUD , and pretty much everything you do in your API centers around doing one of these things to objects, whether they are albums, photos, comments, or users. Although it is not possible to design the perfect API, I will do my absolute best to help you design one that is at least extremely intuitive and doesn’t leave the client authors scratching their heads as to what you intended. Here are some principles to follow when designing RESTful interfaces:

■ There are two basic kinds of URLs. Most URLs you design will be a variation on these two:

1. Collections—for example, /albums.

145 REST API Design and Modules

■ Collections should be nouns, specifically a plural noun, such as albums , users , or photos . ■ PUT /albums.json—The HTTP request body contains the JSON with the data for

the new album.

■ You fetch or update an object by specifying the specific instance on the collection with GET or POST , respectively:

GET /albums/italy2012.json—The request returns this album.

POST /albums/italy2012.json—The HTTP request body contains the JSON with the new data for this album.

■ You destroy objects with DELETE .

DELETE /albums/italy2012.json—Destroys this album.

■ If you have collections off collections, for example, photos associated with albums, you just continue the pattern:

GET /albums/italy2012/photos.json—Returns all the photos in this album. ■ PUT /albums/italy2012/photos.json—Adds a new photo to this collection. ■ GET /albums/italy2012/photos/23482938424.json—Fetches the photo with

the given ID.

■ Slight changes to the way in which you get data from these collections, such as pagination or filtering, should all be done via GET query parameters:

GET /albums.json?located_in=Europe—Fetches only those albums in Europe. ■ GET /albums/japan2010/photos.json?page=1&page_size=25—Fetches the

given page of 25 photos for the japan2010 album.

■ You version the API so that if you want to make major changes to it in future versions that would not be compatible, you can simply update the version number. You prefix the API URLs with /v1/ for this first version.

■ Finally, you suffix all your URLs that return JSON with .json so that clients know what data will be in the responses. In the future, you could also add support for .xml or whatever other formats you want.

With this in mind, the new API for the photo-sharing application centers around albums. It looks as follows: /v1/albums.json /v1/albums/:album_name.json /pages/:page_name /templates/:template_name /content/:filename

Why don’t you put version numbers on the static content URLs? Because they’re not part of the JSON server interface, but instead are helpers to provide the contents for the web browser client. Users always get the latest version of this and thus get code that knows how to use the correct version of the API.

It should be trivial for you to update the server.js file with this API design change. Don’t forget to also update the bootstrapping JavaScript files in /content/ with the new URLs as well! Give that a try before continuing onward.

Modules

With the APIs updated around the new REST interface, you can take advantage of some of the clarity you have given to the application. You now have clear functionality boundaries around albums and pages, so you can put those areas of functionality into their own modules. Create a subfolder of the application folder called handlers/ and put the new modules in there. For albums, create a file in that folder called albums.js and move the four album manipulation functions into that file. You can view the source for this project in the GitHub under the folder named handlers_as_modules/. Effectively, all you are doing is taking the code for the functions handle_get_album and handle_list_albums and putting them, along with their accompany- ing helper functions load_album and load_album_list , into the new module. Take a look at this for a minute and see how easy it is to reorganize the code quickly.

Recall that the server.js file has a few functions to help you extract album names and query parameters and the like. You can get rid of them and just put the appropriate member refer- ences for req.params and req.query in the right place.

There are also a few functions to help you simplify sending success and error codes, such as send_success , send_failure , and invalid_resource . You can put them into their own module called helpers.js in the handlers/ directory. You refer to this at the top of the albums.js file, and the contents are shown in Listing 7.1 .

Listing 7.1 Helper Functions (helpers.js) exports.version = '0.1.0';

exports. make_error = function(err, msg) { var e = new Error(msg);

e.code = err; return e; }

exports. send_success = function(res, data) {

res.writeHead(200, {"Content-Type": "application/json"}); var output = { error: null, data: data };

res.end(JSON.stringify(output) + "\n"); }

147 REST API Design and Modules

exports. send_failure = function(res, code, err) { var code = (err.code) ? err.code : err.name;

res.writeHead(code, { "Content-Type" : "application/json" });

res.end(JSON.stringify({ error: code, message: err.message }) + "\n"); }

exports. invalid_resource = function() { return make_error("invalid_resource",

"the requested resource does not exist."); }

exports. no_such_album = function() { return make_error("no_such_album",

"The specified album does not exist"); }

Now, when you want to refer to the album functions, you add the following require to the top of server.js:

var album_hdlr = require('./handlers/albums.js');

And then you can update the routing handlers for the album functions to be as follows: app.get('/v1/albums.json', album_hdlr.list_all);

app.get('/v1/albums/:album_name.json', album_hdlr.album_by_name);

Similarly, you can move the functionality to build pages into the handlers/ directory in a file called pages.js, as in Listing 7.2 . This task really only involves moving the serve_page function.

Listing 7.2 Building Pages (pages.js) var helpers = require('./helpers.js'),

fs = require('fs'); exports.version = "0.1.0";

exports. generate = function (req, res) { var page = req.params.page_name; fs.readFile(

'basic.html',

function (err, contents) { if (err) {

send_failure(res, 500, err); return;

contents = contents.toString('utf8');

// replace page name, and then dump to output. contents = contents.replace( {{PAGE_NAME}} , page); res.writeHead(200, { "Content-Type": "text/html" }); res.end(contents);

} ); };

You can then update the routing handler for pages, as follows: var page_hdlr = require('./handlers/pages.js');

app.get('/v1/pages/:page_name', page_hdlr.generate);

After all is said and done, the server.js file has left only the functions for serving static content, which you can view in the GitHub source for Chapter 7, “Building Web Applications with Express,” in the handlers_as_modules/ project folder.

The application should have the same functionality as before, but is now much more modular- ized and is easier to read and follow along. You now also have an idea for how you can add functionality to it as you proceed.

In document Learning Node js pdf (Page 159-163)

Related documents