essay on splitting apis

This commit is contained in:
carson 2021-09-16 07:27:24 -06:00
parent 6d85d1ae5b
commit cd8bffeea5

View File

@ -0,0 +1,98 @@
---
layout: layout.njk
tags: posts
title: Splitting Your APIs
---
## Splitting Your APIs
Recently, [Max Chernyak](https://max.engineer/) wrote an essay entitled
[Dont Build A General Purpose API To Power Your Own Front End](https://max.engineer/server-informed-ui). His
TLDR is
> YAGNI, unless youre working in a big company with federated front-ends or GraphQL.
He then discusses some of the different needs of a general purpose API and your application API. He lists the
following as needs for a generic API:
1. How to predict and enable all possible workflows
1. How to avoid N+1 requests for awkward workflows
1. How to test functionality, performance, and security of every possible request
1. How to change the API without breaking the existing workflows
1. How to prioritize API changes between internal and community requirements
1. How to document everything so that all parties can get stuff done
And these as application API needs:
1. How to collect all the data needed to render a page
1. How to optimize requests to multiple endpoints
1. How to avoid using API data fields in unintended ways
1. How to weigh the benefit of new features against the cost of new API requests
I will term this mis-alignment of needs the "Data/App API Impedance Mismatch" problem.
Max's recommendation is to split the API into two "halves", a generic API and an application API:
> I suggest you stop treating your frontend as some generic API client, and start treating it as a half of your app.
>
> Imagine if you could just send it the whole “page” worth of JSON. Make an endpoint for `/page/a` and render the whole JSON for `/page/a` there.
> Do this for every page. Dont force your front-end developers to send a bunch of individual requests to render a complex page.
> Stop annoying them with contrived limitations. Align yourselves.
## Right about What's Wrong
I agree entirely with Max on the problem here. I would particularly emphasise the fact that the generic API needs to
be stable, whereas the application API must change rapidly to address application needs. Jean-Jacques Dubray, in
[this article](https://www.infoq.com/articles/no-more-mvc-frameworks/) relates the following sad state of affairs for
API designers:
> The worst part of my job these days is designing APIs for front-end developers. The conversation goes inevitably as:
> Dev So, this screen has data element x,y,z… could you please create an API with the response format {x: , y:, z: }
> Me Ok
This is a perfect encapsulation of the tension that Max has noticed, where API engineers want to design general,
stable APIs, but are subject to the whims of a quickly-changing UI with complex data needs that are often best
solved on *the server side*.
As Max points out:
> You can keep “page a” dumb to only do what it needs to do. You test the crap out of “page a” for bugs, security, performance. You can even fetch everything for “page a” in a single big SQL query.
## Wrong about What's Right
So, again, I agree entirely with Max on noticing the Data/App Impedance Mismatch problem and applaud him for suggesting
that, rather than bailing out to a solution to like GraphQL, you split the APIs into two.
However, there is a next step here:
Once you have split your application API from your generic data API, you are no longer bound by the constraints of
a public data API and are free to reconsider the *entire form* of that application API. You can do whatever you'd like with
it, so let's get a bit expansive in our thinking.
Note that core problems with the application API are rapid change and page (or resources) specific tuning. It turns out that we
have a very good technology for dealing with this: hypermedia!
Hypermedia, by way of HATEOAS, makes API churn [much less of a problem](https://intercoolerjs.org/2016/02/17/api-churn-vs-security.html). When you change the shape of your hypermedia API
that's fine: the new API is simply reflected in the new HTML returned by the server. You can add and modify end points
and, lo and behold (to a first order of approximation) your clients (that is, browsers) don't need to be updated.
The browsers simply see the new HTML, and [the human driving them react to the new functionality appropriately](https://intercoolerjs.org/2016/05/08/hatoeas-is-for-humans.html).
So, while I feel Max is on the right track, I also think he *doesn't go far enough*: once you have made the mental
jump to solving the Data/APP API Impedence Mismatch by splitting the two, it is only a bit further to rediscovering
the advantages of hypermedia.
You may object that: "Oh, but hypermedia applications aren't very usable, we don't want to go back to web 1.0."
That is a perfectly reasonable objection, but people have been working on that problem and there are now many libraries
available that address the usability issues of HTML *within the hypermedia model*. Two of my favorites are [unpoly](https://unpoly.com/) and,
of course, my own [htmx](/).
If you switch to a hypermedia application API (which really just means "use HTML, like you used to") then you get all
of the benefits of the REST-ful web model (simplicity, reliability, etc.) and of server-side rendering in mature web frameworks
(caching, SQL tuning, etc.)
And, by choosing a hypermedia-oriented front end technology like htmx, you can create [excellent user experiences](/examples) within
that model.
Everything old is new again, but, this time, a little bit better.