By Peter Bell

A REST-like Administrative API

Most of the websites I create require content management, with pages that define a context for multiple dynamic content areas, so on the whole a REST style interface with unique URIs for individual content items and actions on them aren't of much use.

However, for the associated administrative system for managing content items, a REST-like API (not religiously RESTful - see my earlier post for an explanation of why) would solve a bunch of problems. It would make it easy to define the URIs for any given action in a consistent way across applications and would also allow for the testing and calling of ALL model methods using a single, very simple controller. Over time I could also add support for multiple response formats so this could be the basis for everything from lightweight scaffolding to a remote API for Flex applications.

The first step was to come up with the necessary URI templates for defining access to both single objects and collections of 0..n objects (comparable to the difference between a DAO and a Gateway). I decided on different templates for the two to make it clear whether you were operating on a single object or a collection . . .

The Object URI Template
A template for single objects is fairly straightforward. It needs to identify that it operates on a single object (not a collection), it needs to have the object type name, the unique object identifier value, the action and any action qualifier.

To define that it operates on an object, I started the URI with the word “object”. I follow that with the object type name (user, product, invoice, etc.). For the unique object identity value, if you are using the default identity attribute for that object type (typically ProductID, UserID, etc.) you just put a well-formed ID after the object type name. If you want to use a different unique identity (some object types have multiple independently unique identity fields – e.g both UserID and UserEmail) for a given call, you can prepend the value with the name of the unique identity attribute, allowing you to return a user by email address or a product using its SKU. I decided on a pipe (|) as the delimiter between the optional unique identity attribute name and the required unique attribute value as very few unique identities include pipes and pipes are URL safe. Then I just added the action.

Four verbs (actions) just aren’t enough for me, so I allow for n-verbs, and then I have the optional action qualifier which allows you to say WHICH get or delete method you want to run (distinguishing getting a fully loaded object from a method that might just return enough attributes for displaying a users first name, last name and email, for instance) where you don’t want to just use the default method.

For anyone following along with my base classes, the actions (verbs) correspond to public base methods and the action qualifiers would call object specific methods that extend the base methods.

TEMPLATE for single objects:
/object/{ObjectTypeName}/{Optional: IdentityField |}{IdentityValue}/{Action}/{Optional: ActionQualifier}

Examples
/object/user/13/edit
/object/product/SKU|TTF612/get
/object/user/email:test@test.com/get/basicinfo
/object/product/31/delete
/rest/object/newsletter/0/add/quick

The Collection URI Template
Collection URIs must identify that they operate on a collection, identify the object type, describe the action and optionally provide an action qualifier.

So, they start with “collection” and then continue with the object type (product, user, article, etc.), the action, and any action qualifiers where there are multiple object specific methods for that action. For example, the “get” action for products could have “All” and “InCategory” action qualifiers.

TEMPLATE for collections of objects:
/collection/{objectname}/{verb}/{verb qualifier}

Examples
/rest/collection/newsletter/list /rest/collection/user/get/all /rest/collection/newsletter/list/full

Of course, that still leaves the question of how (it at all) to handle state/security, and how to provide data to object calls (e.g. a save) and parameters to collection calls (e.g. a getbyCategory() needs the category ID and a getbyFilter() might need the filter, order and optionally the page number and records per page).

I’ll post on REST-like security and REST-like parameters separately to avoid turning this post into a book :->

Oh, BTW, for anyone interested in REST, Sean Corfield created a tool for calling any cfc using an HTTP Post. As Patrick Correia pointed out on one of the earlier referenced posts (which you should read) it's more RPC than REST as there isn't a unique URI for each method and content item, but it is still a cool tool and a GREAT example of how a project should look (note that the test class is substantially larger than the class it is testing)!

Comments
BlogCFC was created by Raymond Camden. This blog is running version 5.005.