If you want developers to love your API, focus the bulk of your efforts on designing a beautiful one. If you want to boost adoption of your API, consider a tool to make life easier on your users: client libraries. Specifically, Node.js client libraries.

This series will cover our playbook for building a stable, useful Node.js client in detail, in three parts:

Part 1: Need-to-know RESTful Concepts

Part 2: REST client design in Node.js

Part 3: Functionality

If you would like to learn general REST+JSON API design best practices, check out this video. In this article series, we are going to focus exclusively on client-side concepts.

Lastly, keep in mind that while these articles use Node.js, the same concepts apply to any other language client with only syntax differences.

OK. Let’s begin with the RESTful concepts that make for a killer client. After all, if we don’t nail these down, no amount of JavaScript will save us later.

HATEOAS

HATEOAS , usually pronounced ‘Haiti-ohs’, is an acronym for “Hypermedia As The Engine of Application State”. Aside from being an unfortunate acronym, HATEOAS dictates that REST API clients should not know anything about that REST API: a REST client should issue an initial request and everything it needs from that point on can be discovered from the initial response.

HATEOAS is still considered the ideal target for REST API design, but at times, HATEOAS will present a challenge to our REST client design efforts. Think of it this way:

Your end-users will want to use your shiny new client to do the things they know are possible in your API. They will want to invoke known / defined behavior. You will want to provide them convenience functions to make it super easy for them to interact with your API – things that may not be as easy or possible with standard HTTP requests. Reconciling 1 and 2 with HATEOAS simply won’t always possible.

Our philosophy takes the pragmatic view: while HATEOS is ideal for automated software agents (like browsers), it is often not as nice for humans that want library functions that address specific needs, so we will diverge from HATEOAS when it makes sense.

REST Resources

Resources transferred between the client and the API server represent things (nouns), not behaviors (verbs). Stormpath is a User Management API, so for us, resources are records like user accounts, groups, and applications.

No matter what your API’s resources are, each resource should always have it’s own canonical URL. This globally unique HREF will identify each resource and only that resource. This point really can’t be stressed enough; canonical URLs are the backbone of RESTful architecture.

In addition to having a canonical URL, resources should be coarse-grained. In practice, this means we return all resource properties in their entirety in the REST payload instead of as partial chunks of data. Assumptions about who will use the resource and how they will use it only make it more difficult to expand your use cases in the future. Besides, coarse-grained resources translate to fewer overall endpoints!

Collection Resources

Collection resources are first-class citizens with their own first-class properties. These properties contain data that in turn describe the collection itself, such as limit and offset .

Collection resources should have a canonical URL and follow a plural naming convention, like /applications (and not /application ) to make identification easy. A collection resource always represents potentially many other resources, so plural naming conventions are the most intuitive.

Collections usually support create requests as well as query requests, for example, ‘find all children resources that match criteria X’.

Instance Resources

An instance resource is usually represented as a child of some parent collection. In the example below, the URI references a particular application within the /applications collection. The implication is that if you interact with this endpoint, you interact with a single application.

/applications/8sZxUoExA30mp74

For the most part, instance resources only need to support read , update , and delete operations. While not a hard and fast rule, reserving create for collections is a common convention in REST API design, especially if you want to generate unique identifiers for each newly-created resource.

Resource Code Examples

Ok, now the fun stuff – code!

If we are to translate these REST resource concepts to working code, it would make sense to have code artifacts that represent both collection and instance resources.

Here’s an example of what it might look like to define a very general resource concept in a Node.js library:

var util = require('util'); function Resource(...) { ... } util.inherits(Resource, Object); someResource.href 1 2 3 4 5 6 7 var util = require ( 'util' ) ; function Resource ( . . . ) { . . . } util . inherits ( Resource , Object ) ; someResource . href

We’re using JavaScript’s prototypical inheritance here to simulate classical Object Oriented inheritance. We’ve found this to be the easiest to understand abstraction for most developers using code libraries, so we went with this paradigm.

As you can see, The Resource ‘class’ above takes advantage of the standard Node util library to create a resource constructor function. If you want to simulate a classical OO hierarchy, util is a great way to do it.

Next, we’ll extend this general resource class to create more specific Instance and Collection resource classes.

function InstanceResource(...) {...} util.inherits(InstanceResource, Resource); anInstanceResource.save(function (err, saved) { ... }); anInstanceResource.delete(function (err) { ... }); 1 2 3 4 5 6 7 8 9 10 11 function InstanceResource ( . . . ) { . . . } util . inherits ( InstanceResource , Resource ) ; anInstanceResource . save ( function ( err , saved ) { . . . } ) ; anInstanceResource . delete ( function ( err ) { . . . } ) ;

As mentioned, you’ll notice save and delete methods on InstanceResource , but no create . The callback on save returns either an error or the successfully saved object; delete has no object to return, so only an error might be provided to the callback. Both methods are called asynchronously after the operation is complete.

So what’s the takeaway? You can save or delete individual things, but not necessarily entire collections. Which leads us to our next resource class:

function CollectionResource(...) {...} util.inherits(CollectionResource, Resource); aCollResource.each(function (item, callback) { ... }, function onCompletion(err) { ... }); aCollResource.eachSeries aCollResource.map aCollResource.filter ... other async.js methods ... 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function CollectionResource ( . . . ) { . . . } util . inherits ( CollectionResource , Resource ) ; aCollResource . each ( function ( item , callback ) { . . . } , function onCompletion ( err ) { . . . } ) ; aCollResource . eachSeries aCollResource . map aCollResource . filter . . . other async . js methods . . .

CollectionResource can support a number of helper functions, but the most common is each . each takes an iterator function which is invoked asynchronously for every instance in the collection.

applications.each(function(app, callback){ console.log(app); callback(); }, function finished(err) { if (err) console.log(‘Error: ‘ + err); }); 1 2 3 4 5 6 7 applications . each ( function ( app , callback ) { console . log ( app ) ; callback ( ) ; } , function finished ( err ) { if ( err ) console . log ( ‘ Error : ‘ + err ) ; } ) ;

This example uses each to simply log all the instance resources.

As a great convenience, we made the decision early on to assimilate all of async.js’ collection utility functions into all Collection resources. This allows developers to call Stormpath methods using the semantics of async.js and allows the client to delegate those methods to the corresponding async.js functions behind the scenes. Eliminating even just one package to import has proven to be really convenient, as we’ll discuss more in part 3. We’re big fans of async.js.

(Note that async.js requires you to invoke a callback method when you’re done with a given iteration step.)

That’s it for the RESTful groundwork! We have a number of other articles and videos pertaining to REST security, linking, POST vs PUT, and more on the blog if you’re interested.

Part two of this series will be all about the nitty-gritty details of coding the client. Expect sections on encapsulation, public vs. private API implementation, and our component architecture. Stay tuned!

API Management with Stormpath

Stormpath makes it easy to manage your API keys and authenticate developers to your API service. Learn more in our API Key Management Guide and try it for free!