Tuesday, August 6, 2013

Multiple content representations from a resource oriented RESTful web service

Here are a some thoughts on a few ways you can return multiple different representations of your resources from RESTful web services and still preserve the resource oriented nature of your architecture.

First, by representational differences I'm not talking about the format (JSON vs. XML, etc.). I'm talking about content.

Keep in mind that under the ROA style for REST you can use query params for selection, sorting and projection. Selection answers the question of which instances of the resource to return (which rows in database terms). Sorting is self-explanatory. Projection refers to which parts of the resource (which data points, or columns in database terms) to return.

So, when we're talking about multiple representations with respect to query params we're talking about projection.

Let's consider an example of a representation of a Customer resource with the following data points:
  • customer_id
  • first_name
  • last_name
  • address1
  • address2
  • city
  • state
  • zip
  • zip_plus_4
  • home_phone
  • mobile_phone
  • birth_date
  • birth_place
  • email_address
  • income
Now, imagine that the following URL returns a complete representation of the above Customer resource for a customer with customer_id 123:

        http://www.my-company.com/resources/customer/123

You will notice that we are doing selection, but we aren't using query params but rather putting the customer_id on the URL itself, which is a cleaner approach to REST.

Use projection via a query param

Now, what if a given client didn't want to consume all those data points and endure all the overhead associated with that.  If using a query param approach you could do something like this:

        http://www.my-company.com/resources/customer/123?include=customer_id,last_name,zip,email_address

The web service implementation for this would process the 'include' query param and build up a resource that included only those data points specified.  Under this approach you give the client maximum control of the resource representation.

Extract a sub-resource

Another way to obtain a subset of the Customer resource would be to extract a sub-resource.  For example, imagine we were only interested in the customer contact info consisting of customer_id, first_name, last_name, mobile_phone and email_address.  Then, we could use a URL like the following to obtain the contact information for the customer:

        http://www.my-company.com/resources/customer/123/contact_info

But, we've created a new URL endpoint, which may or may not be what we want.  How can we isolate the contact information without using query params and without changing the original customer URL?

Define a custom media type

Let's say we had defined a media type for the Customer resource as so:

        application/vnd.my-company.Customer-1.0

The client would pass this in as the Accept header to fetch the complete representation.  To isolate the contact information we could define a new media type like so and pass that in as the Accept header with the original URL:

        application/vnd.my-company.Customer.ContactInfo-1.0

Now, let's say the client is happy with the original customer representation, but wants to trim the size of it.  We could create a 'lite' version with abbreviated attribute names, such as lname for last_name, email for email_address and so on, and use a media type like the following to retrieve it:

        application/vnd.my-company.Customer-1.0-lite

You should be able to see the flexibility that custom media types provide.  You could create many different subsets of customer information and expose those as different flavors of the Customer media type.

Each of the above relies on being able to vary the resource representation independently from any object model supporting it.  See this article for more information.

No comments: