digitaljoel
digitaljoel

Reputation: 26584

Dynamic representation of a REST resource

Lets assume I have an object that I expose as a REST resource in my application. This object has many fields and contains many other objects including associated collections. Something like this, but think MUCH bigger:

Customer
  List<Order> orders
  List<Address> shippingAddresses;
  // other fields for name, etc.

Order
  List<Product> products
  // fields for total, tax, shipping, etc.

Product
  // fields for name, UPC, description, etc.

I expose the customer in my api as /customer/{id}

Some of my clients will want all of the details for every product in each order. If I follow HATEOAS I could supply a link to get the product details. That would lead to n+1 calls to the service to populate the products within the orders for the customer. On the other hand, if I always populate it then many clients receive a bunch of information they don't need and I do a ton of database lookups that aren't needful.

How do I allow for a customer representation of my resource based on the needs of the client?

I see a few options.

  1. Use Jackson's JsonView annotation to specify in advance what is used. The caller asks for a view appropriate to them. i.e. /customer/{id}?view=withProducts. This would require me to specify all available views at compile time and would not be all that flexible.

  2. Allow the caller to ask for certain fields to be populated in the request, i.e. /customer/{id}?fields=orders,firstName,lastName. This would require me to have some handler that could parse the fields parameter and probably use reflection to populate stuff. Sounds super messy to me. The what do you do about sub-resources. Could I do fields=orders.products.upc and join into the collection that way? Sounds like I'm trying to write hibernate on top of REST or something.

  3. Follow HATEOAS and require the client to make a million HTTP calls in order to populate what they need. This would work great for those that don't want to populate the item most of the time, but gets expensive for someone that is attempting to show a summary of order details or something like that.

  4. Have separate resources for each view...

  5. Other?

Upvotes: 2

Views: 199

Answers (2)

Chris Betti
Chris Betti

Reputation: 2923

Option 2 (client specifies fields) is a filtering approach, and acts more like a query interface than a GETable resource. Your filter could be more expressive if you accept a partial template in a POST request that your service will populate. But that's complicated.

I'm willing to bet all you need is 2 simple representations of any complex entity. That should handle 99.9% of the cases in your domain. Given that, make a few more URIs, one for each "view" of things.

To handle the 0.1% case (for example, when you need the Products collection fully populated), provide query interfaces for the nested entities that allow you to filter. You can even provide hypermedia links to retrieve these collections as part of the simplified representations above.

Upvotes: 1

Kinjo
Kinjo

Reputation: 1456

I would do something like this: /customers/{id}/orders/?include=entities

Which is a kind of a more specific variation of your option 1.

You would also have the following options:

  1. Specific order from a specific customer without list of products: /customers/{id}/orders/{id}
  2. Just the orders of a customer without products: /customers/{id}/orders/

I tend to avoid singular resources, because most of the time or eventually someone always wants a list of things.

Upvotes: 1

Related Questions