Designing APIs often leads to a compromise between producers and/or consumers. Consumers of APIs would like the interface to be as close to their requirements as possible; they want to avoid possible composition or contract navigation. API producers would like their interface to be as simple as possible; they want to support maximum reuse and consumption.
I’ve touched on this in a previous article, Should APIs be Pre-made or Deli style?, where I offered different options for consumer flexibility or producer simplicity.
Pre-made. Defining API interfaces based on specific consumer needs - simple for each consumer, but not necessarily reusable.
Deli-style. Defining an API interface that allows consumers to select the attributes they need via arguments in the request - maximum flexibility for the consumer, but at a cost for the producer.
Providing the entire model as one interface. Allowing the consumer to pick the elements they want from the response and discard the rest - simple for the producer, requires contract navigation for the consumer.
For this discussion, let’s look at how we can make the third option a little more flexible for the consumer while keeping it simple for the producer.
Assume this is the entire model of a car:
It’s a fairly
simple model, but I’ll use it to explain the approach.
A producer could expose the entire model via GET: /cars/1234abcd. This would force the consumers to download the entire model and find the values they need. It’s the simplest for the producer, but will probably get challenged by consumers if the model is large.
An alternative is to expose each sub-object as a separate URI – giving consumers the basic information about the car and links to allow them to discover more about the model through hyperlinks (HATEOUS).
This reduces the payload of the response, and allows consumers to retrieve the additional data they might need independently. From a producer’s perspective; each object can be managed in isolation, it’s still simple and supports maximum reuse. For the producer it offers flexibility and reduced payload.
However, even this simple approach leads to another consumer argument: “What if I need more than one object? I will have to make repeated calls to compose all the data I need!”. By example, assuming a consumer was interested in a car’s engine and body, this would result in the consumer having to make three API calls:
Is there another alternative here?
What about using the HTTP URL Fragment? By definition (RFC 3986): “The fragment identifier component of a URI allows indirect identification of a secondary resource …”. This is donated in a URL as the “#” (hash) symbol and allows users to navigate to a specific portion of an HTML document.
We could use the HTTP URL fragment to identify secondary objects within a model, and allow the API consumer to define which objects in the model they are interested in. Since we already have these objects exposed via end-points, we can offer a short-circuit URI using the fragment, as follows:
The resource in question is still the car, but the consumer is asking for additional sub-resources of the car known by “engine” and “interior”.
Obviously, only requesting one object via a fragment is redundant, but I believe this does offer a lot more flexibility for the consumer without introducing too much complexity for the producer.
Let me know your thoughts.