I was out looking for a sandwich for lunch the other day and found myself faced by two options; I could either go to the convenience store and pick up a pre-made sandwich or go to the deli and have one made for me.
The pre-made option sounds simple, but choice is usually limited to the imagination of the sandwich maker. Going to a deli to have a sandwich made offers freedom of choice, but I’d have to deal with queues of people trying to make up their minds about which
ingredients they want (including me when I get there).
From the stores perspective, the pre-made option is easy – display what’s on offer and let the customer pick one; it’s quick and simple. However, a reduced choice may push customers towards the Deli. The Deli owner can offer his customers choice, but at
the cost of extra staff to make sandwiches based on each customers requirement. The little effort required by customers and possible delays could push customers to the convenience store for the “best fit” option.
Producing read APIs could follow a similar approach.
Pre-made. As an API producer, using the inside-out-approach, I could decide “what” my consumers need and make a range of specific endpoints for them to consume. If one endpoint does not cover all their requirements, they could compose the
data they need by consuming a number of my APIs; extracting the bits needed from each. This will give me full control over my API implementation and allow me to optimize them for better performance. Eventually, however, my consumers will complain and ask for
more “specific” APIs to meet their needs. In my experience, this is all too often the reality.
Deli style. As an API producer, using the outside-in-approach, I could offer my consumers the ability to select which elements in my API domain should be returned. This would require me to provide proper documentation to explain my domain
and I’d need to implement the selection logic (and consequences) in my services. Like the Deli needs to maintain the cost of a sandwich maker, I’d need to accept the cost of maintaining this logic and implementing it for new elements in the future. The benefit,
of course, is that my API will offer complete customization, making it as consumable as possible by more than one consumer – a desirable trait for APIs.
There is an alternative – I could provide the full data object in the response. In other words, provide all the data elements of the object being modeled and let the customer extract the values needed, ignoring the rest (pun intended). This approach creates
less work for the producer – only one simple endpoint is required per object. The effort is passed to the consumer, similar to the Deli-style approach, only the selection of elements is made on the response instead of providing input into the request. Taking
advantage of HATEAOS will reduce the payload size and offer discoverability but could force the consumer to perform the composition in order to get all the data elements required.
So, as API producers we have options. We can offer our consumers flexibility through choosing data elements before or after request, or we can offer them what we think they need – the choice is ours. Each approach has its merits and its consequences, but
they are nonetheless choices.
I’ve worked on APIs that follow all three of these approaches. The Deli style approach offers benefit to the consumer and reduces payload. The challenge comes in selecting data elements for the request input in a hierarchical data structure. The approach
we took was simple; use dot.notation to identify the data elements just as you would when reading the JSON response.
Image provided by Pexels.com