76

I am currently designing the API for an existing PHP application, and to this end am investigating REST as a sensible architectural approach.

I believe I have a reasonable grasp of the key concepts, but I'm struggling to find anybody that has tackled object hierarchies and REST.

Here's the problem...

In the [application] business object hierarchy we have:

Users 
 L which have one-to-many Channel objects
 L which have one-to-many Member objects
 

In the application itself we use a lazy load approach to populate the User object with arrays of these objects as required. I believe in OO terms this is object aggregation, but I have seen various naming inconsistencies and do not care to start a war about the precise naming convention </flame war>.

For now, consider I have some loosely coupled objects that I may / may not populate depending on application need.

From a REST perspective, I am trying to ascertain what the approach should be. Here is my current thinking (considering GET only for the time being):

Option 1 - fully populate the objects:

GET api.example.com/user/{user_id}

Read the User object (resource) and return the User object with all possible Channel and Member objects pre-loaded and encoded (JSON or XML).

PROS: reduces the number of objects, no traversal of object hierarchies required
CONS: objects must be fully populated (expensive)

Option 2 - populate the primary object and include links to the other object resources:

GET api.example.com/user/{user_id}

Read the User object (resource) and return the User object User data populated and two lists.

Each list references the appropriate (sub) resource i.e.

api.example.com/channel/{channel_id}
api.example.com/member/{member_id}
    

I think this is close to (or exactly) the implications of hypermedia - the client can get the other resources if it wants (as long as I tag them sensibly).

PROS: client can choose to load the subordinates or otherwise, better separation of the objects as REST resources
CONS: further trip required to get the secondary resources

Option 3 - enable recursive retrieves

GET api.example.com/user/{user_id}

Read the User object and include links to lists of the sub-objects i.e.

api.example.com/user/{user_id}/channels
api.example.com/user/{user_id}/members

the /channels call would return a list of channel resources in the form (as above):

api.example.com/channel/{channel_id}
    

PROS: primary resources expose where to go to get the subordinates but not what they are (more RESTful?), no requirement to get the subordinates up front, the subordinate list generators (/channels and /members) provide interfaces (method like) making the response more service like.
CONS: three calls now required to fully populate the object

Option 4 - (re)consider the object design for REST

I am re-using the [existing] application object hierarchy and trying to apply it to REST - or perhaps more directly, provide an API interface to it.

Perhaps the REST object hierarchy should be different, or perhaps the new RESTful thinking is exposing limitations of the existing object design.

Any thoughts on the above welcomed.

Henke
  • 4,445
  • 3
  • 31
  • 44
paulkmoore
  • 3,253
  • 2
  • 21
  • 20
  • In my searching I've also found this set of articles which I found very useful and accessible: http://www.infoq.com/minibooks/emag-03-2010-rest – paulkmoore Oct 06 '10 at 10:18
  • If you're looking for a JSON-based media type for all of those styles, consider Shoji: http://www.aminus.org/rbre/shoji/shoji-draft-02.txt – fumanchu Oct 06 '10 at 17:45

4 Answers4

45

There's no reason not to combine these.

  • api.example.com/user/{user_id} – return a user representation
  • api.example.com/channel/{channel_id} – return a channel representation
  • api.example.com/user/{user_id}/channels – return a list of channel representations
  • api.example.com/user/{user_id}/channel_list – return a list of channel ids (or links to their full representations, using the above links)

When in doubt, think about how you would display the data to a human user without "API" concerns: a user wants both index pages ({user_id}/channel_list) and full views ({user_id}/channels).

Once you have that, just support JSON instead of (or in addition to) HTML as the representation format, and you have REST.

Pi Delport
  • 10,356
  • 3
  • 36
  • 50
  • Piet - many thanks for this, it is very useful. I would like to clarify the point about traversing the data from a human perspective. My thought is that if I provide a list of 'next' resources rather than a 'full view' I allow the user to peruse what they want to peruse, and therefore providing a list of links is 'more' RESTful (conforms better to HATEOS). I could understand that providing a populated list is useful for performance reasons however. I know this is somewhat use-case dependent, but I wonder if I'd be better to keep the interface simpler (and maybe slower) for the time being? – paulkmoore Oct 06 '10 at 10:14
  • 3
    How do you create such hierarchical objects? e.g. let's say we have library -> books -> chapters -> pages. Now one approach would be to create a bulky library object but what's the alternative? Should you create library first, get the id and then book and so on. – Pradeep Kumar Mishra Jun 20 '12 at 03:26
  • Qiuck question: Does the first @Path or RequestMapping match get everything? If so, the second API will never be executed, right? Should I put the more general API last in my code or first? – – Steve Stilson Jul 01 '20 at 04:09
30

The best advice I can give is to try and avoid thinking about your REST api as exposing your objects. The resources you create should support the use cases you need. If necessary you might create resources for all three options:

api.example.com/completeuser/{id}
api.example.com/linkeduser/{id}
api.example.com/lightweightuser/{id}

Obviously my names are a bit goofy, but it really doesn't matter what you call them. The idea is that you use the REST api to present data in the most logical way for the particular usage scenario. If there are multiple scenarios, create multiple resources, if necessary. I like to think of my resources more like UI models rather than business entities.

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • Darrel - thanks for this - also very useful. I suspected that there was some subtlety around the object mapping that I was missing. I am concluding that whilst I may call on my existing object framework, I need to design carefully my 'resource view'. Thanks. – paulkmoore Oct 06 '10 at 10:16
11

I would recommend Restful Obects which is standards for exposing domain model's restful

The idea of Restful Objects is to provide a standard, generic RESTful interface for domain object models, exposing representations of their structure using JSON and enabling interactions with domain object instances using HTTP GET, POST, PUT and DELETE.

According to the standard, the URIs will be like:

  • api.example.com/object/user/31
  • api.example.com/object/user/31/properties/username
  • api.example.com/object/user/31/collections/channels
  • api.example.com/object/user/31/collections/members
  • api.example.com/object/user/31/actions/someFunction
  • api.example.com/object/user/31/actions/someFunction/invoke

There are also other resources

  • api.example.com/services
  • api.example.com/domain-types

The specification defines a few primary representations:

  • object (which represents any domain object or service)
  • list (of links to other objects)
  • property
  • collection
  • action
  • action result (typically containing either an object or a list, or just feedback messages)
  • and also a small number of secondary representations such as home, and user

This is interesting as you’ll see that representations are fully self-describing, opening up the possibility of generic viewers to be implemented if required.

Alternatively, the representations can be consumed directly by a bespoke application.

Yasir Hantoush
  • 711
  • 6
  • 4
6

Here's my conclusions from many hours searching and with input from the responders here:

Where I have an object that is effectively a multi-part object, I need to treat that as a single resource. Thus if I GET the object, all the sub-ordinates should be present. This is required in order that the resource is cacheable. If I part load the object (and provide an ETag stamp) then other requestors may receive a partial object when they expected a full one. Conclude - objects should be fully populated if they are being made available as resources.

Associated object relationships should be made available as links to other (primary) resources. In this way the objects are discoverable by traversing the API.

Also, the object hierarchy that made sense for main application site may appear not be what you need to act in RESTful manner, but is more likely revealing problems with the existing hierarchy. Having said this the API may require more specialised use cases than had been previously envisaged, and specialised resources may be required.

Hope that helps someone

paulkmoore
  • 3,253
  • 2
  • 21
  • 20