Let's stop building APIs around a network hack (2017)
apisyouwonthate.com
apisyouwonthate.com
As far as I can see, the argument is "http2 removes handshake overhead and therefore you don't need to combine multiple things in a single response anymore".
If my request takes a second to go around the world and back, how does http2 help me? It's very weird to me to read an article about API design, written in an expert tone, that does not address this.
Most advice I've heard from REST aficionados tends to be terrible for simplicity, correctness and performance. All in favor of an abstract set of goals, most of which never materialize. This is no exception.
https://apisyouwonthate.com/blog/rest-and-hypermedia-in-2019
That's a post which aims to explain the tangible benefits of each step, but I've got plenty more articles explaining when REST is overkill and simple RPC would be a better fit.
Articles like this talk about a world I don’t understand anyway. It’s like the rest/hateois etc. stuff. I just make an MVC action that does what I need in a way that makes sense and call it from the browser. If shit is slow, I profile it. No highfalutin design philosophy!
https://www.fastly.com/blog/optimise-api-cache-improved-perf...
Both in terms of latency (to be fair, probably pretty small in comparison), and in terms of knowing that all the data you fetched is consistent (because you fetched it in the same transaction).
Sure, edge computing is a thing but if all the edge workers still need to go to some read replica database at least half a continent away, then in the end the 1+N requests the author suggests aren't going to get you very far.
https://phil.tech/2020/prioritizing-api-ecosystem-maturity/
No my article was not ignoring the concept of latency. Let fastly try and make the same points better.
https://www.fastly.com/blog/optimise-api-cache-improved-perf...
Your reply assumes that API requests need to be made in sequence. If you need 3 resources, and you make 3 requests, you are not waiting for 1 then 2 then 3. 1 and 2 and 3 are all coming back along the multiplexed connection and none are blocking the other. That is generally what a lot of compound documents are doing, but sure the article also talks about when you need to know some information from 1 before you can call 2 or 3.
- Server Push - since axed from some browsers, but the idea there is to proactively push the resource to you before you request it, because you know people often request Bar after requesting Foo. That means its not 1s it could be 100ms or less. The GraphQL community took this idea and created @defer.
- If people ALWAYS need to request 1 and 2 and 3, that API sounds like it was overly normalized and not too well planned. Maybe its more of a "Data Store" than an API.
It also talks about how by making endpoints smaller and more targeted, the cachability of one resource does not impact another, so you dont need to wait 1s for each requests, you just skip a lot of the requests. This article covers a lot of the same points.
https://www.fastly.com/blog/optimise-api-cache-improved-perf...
If you fetch invoices, and after receiving them you fetch ids of order items, and only after that you fetch actual order items, it will take 3x longer, than if you fetch everything in a single request. This can mean it could take 3 seconds to get the result, instead of 1 second with GraphQL.
In something truly RESTful (with HATEOAS and media types and all that jazz), the links between resources are only discoverable dynamically, and the client traverses that resource graph interactively. As the article explains, that works a lot better when you aren't scared of the per-request network overhead. But it still fundamentally limits the amount of context provided to the server on each interaction, preventing the server from optimizing for each request beyond guessing and pre-fetching what it thinks a client will need. (And the pre-fetched data has to live somewhere, meaning you've just created a cache and all its concomitant issues.)
Taking GraphQL as an alternative, the resource graph is statically known -- you can effectively fetch the schema just once. You can then traverse through many resource relationships and fetch many disparate units of data in a single request, and the backend can see that structure and decide how to efficiently gather all of that data.
Even besides the interaction context, doing REST properly (instead of a faux-REST actually-RPC HTTP API) can be pretty challenging. How many REST APIs are founded on media types instead of statically-determined URLs and methods? [0] If you're just doing HTTP RPC, you more-than-likely have a statically defined API schema, but no way to leverage that static knowledge for efficiency. (Instead you get clients coupling to the specific API endpoints instead of to the media types, making it not just static but also rigid.)
Also, HTTP/2 Push (mentioned in the article) seems to be in a weird place right now. [1]
[0] https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
So yes, the L2 will do all it can do hides wireless issues to the upper layers. But it cannot make miracles and wireless is still a challenging medium for end-to-end congestion control. There's still work at the TCP (or let's say end-to-end congestion control layer, with QUIC) to improve things, see BBRv2 for example. It's an active area, and any new generation tends to push the current state of the art.
[1] https://en.wikipedia.org/wiki/Automatic_repeat_request [2] https://en.wikipedia.org/wiki/Hybrid_automatic_repeat_reques...
you speak like there's anything that exists outside of a 70km-long band in California
GET /invoices?invoicesQuery=criteria
GET /invoices/orders?invoicesQuery=criteria
This doesn't take longer than graphql (because you're not waiting for the first response) and is a lot simpler.Shopify's REST API is way easier to use, it's just slower. If pipelining solved the latency issue, it would be an all round superior experience.
I would expect server push to be basically unused until something comes around to automate it. It seems totally plausible to me, for example, that NGINX might add some logic to automatically keep track of which resources are requested after which other resources, and, for example, notice that "/style.css" is requested after "/" 99.9% of the time, and therefore automatically push the stylesheet.
It says a lot about Google if their approach to this stuff is to build something which requires the entire world of web servers to adapt, and then rip out the feature if it doesn't take off right away. If they were serious about it, getting everyone to restructure how their infrastructure works should've been treated as a decades-long project.
This just reinforces my existing view of not trusting Google for anything important. They just can't keep at a project. Imagine being the sucker who suggested that a business should spend a load of time restructuring their back-end infrastructure to support server push, just for Google to rip it out.
The main problem is that tertiary data slows down primary data.
Put simply, when you request 3 things, it might take a little while for the server to find process the response, create all the JSON, shove it all over the wire, and be picked up by your client application.
The user has been sat there waiting for all three things to come back at once.
Real world example: a social network full of posts and images is failing to load because the application wanted to know if there was any space left in a meeting room nearby.
If you have 3 different things they probably have different cacheability, so let them declare their own cacheability and let the client only bother fetching them if they need to. Show the things you have when you get them. Progressively enhance the page. Don't just show nothing.
There's a lot of solutions to various problems with compound document systems like JSON:API and I've written about those too: https://apisyouwonthate.com/blog/making-the-most-of-jsonapi
But the core purpose of compound documents was the same thinking as CSS combination and Image Sprites, most of which have gone away now, and I think compound documents should too. They were a handy hack that had downsides and its no longer necessary to mess with them.
You need to know the mess that was SOAP before you go into REST, like you need to know about XML before you criticize JSON.
JSON is not a good place to put anything but data, to specify an API with a meta language has been disproven by SOAP already.
My approach to end all API fragmentation is to simply use JSON over HTTP with minimalism!
I do not conform to REST, but instead exclusively use GET and POST and return 200 everywhere and handle everything manually from there.
I also make HTML forms to all API entrypoints so you can test and learn how the API works by browsing and viewing source.
All errors are 200 unless they are HTTP errors. The backend error is under the errors key in the json.
All post requests.
Trying to merge the application API and HTTP concepts (like HTTP methods and HTTP response codes) seems to mostly just be a REST thing. Most other APIs treat HTTP as a transport layer in my experience.
They don't? I'd assumed that was essential to anything that did a lot of back and forth transactions. And you certainly don't have to do a DNS lookup for each call.
But in HTTP/1.1 that's the default: https://tools.ietf.org/html/rfc7230#section-6.3
o If the received protocol is HTTP/1.1 (or later), the connection will persist after the current response; else,
o If the received protocol is HTTP/1.0, the "keep-alive" connection
option is present, the recipient is not a proxy, and the recipient
wishes to honor the HTTP/1.0 "keep-alive" mechanism19.7.1 Compatibility with HTTP/1.0 Persistent Connections
Some clients and servers may wish to be compatible with some previous
implementations of persistent connections in HTTP/1.0 clients and
servers. Persistent connections in HTTP/1.0 must be explicitly
negotiated as they are not the default behavior. HTTP/1.0
experimental implementations of persistent connections are faulty,
and the new facilities in HTTP/1.1 are designed to rectify these
problems. The problem was that some existing 1.0 clients may be
sending Keep-Alive to a proxy server that doesn't understand
Connection, which would then erroneously forward it to the next
inbound server, which would establish the Keep-Alive connection and
result in a hung HTTP/1.0 proxy waiting for the close on the
response. The result is that HTTP/1.0 clients must be prevented from
using Keep-Alive when talking to proxies.In the original Fielding paper, REST is explicitly defined as an architecture for "large-grained resources" aka "documents". So this "hack" the author perceives is inherent to REST.
If your API is small-grained (like most HTTP/web/JSON APIs are), then Fielding explicitly states you shouldn't be using REST.
Quite interestingly and not well-known to many it seems, HTTP/1.1 had many HTTP/2-like proposals like MULTIHEAD and MULTIGET methods. Fielding rejected them on the basis they're not RESTful.
The current praise of HTTP/2 as a major win for REST APIs shows how differently "REST" is understood by its proponents today compared to its original meaning. If doing REST today is about doing the opposite of what it was intended to do, it also raises the question why do we believe we get the same benefits REST did for its original definition? A quick read shows we don't, but the cultural ingraining in favor of "believing" is huge.
TLDR; Nothing to see here, keep doing what works.
P.S.: Server push has been deprecated, it's basically dead.
REST as an architecture has a lot more in common with how we users traverse the web than how we imagine our machines to query an API. We go from page to page and interpret the content on each page to decide which link to follow next. The HTML media type is understood by our browsers, so we're presented with a consistent way of traversing, submitting forms, etc. that behaves consistently across any site that provides an HTML resource. And a website can update its pages as much as it wants, and as long as it's still providing HTML, a user can learn to navigate it again.
Of course, that genericity and reliance on human learning is exactly what makes HTML alone not a good media type for machine-to-machine REST APIs, but that's why media types are (supposed to be) a cornerstone of REST: they describe the schematic structure of a general kind of resource, specifying how such resources may relate to other resources and what other kinds of data they might contain, but leave it up to the individual resources to actually make use of those facilities (and up to the client to reason against those facilities).
But try finding a "RESTful" API that actually does that. Most of the ones I've seen actually use a bank of statically-known URLs that report a content-type of JSON whose actual fields are statically-known by separate specification. It doesn't really matter whether you're using HTTP or not, that's just RPC.
> I wish anything built on REST would die. Conflation of the envelope and the payload is idiotic and adds only pain, no value.
Anyway, a reasoned conversation about where REST can be useful even today: https://apisyouwonthate.com/blog/rest-and-hypermedia-in-2019
And a technical comparison for when people need it: https://apisyouwonthate.com/blog/understanding-rpc-rest-and-...
And when those technical differences can come in useful or just be annoying: https://apisyouwonthate.com/blog/picking-the-right-api-parad...
Loooooots of content on all this. :)
I'm very interested in getting GraphQL features into http and wasn't aware of these prior proposals. A quick web search didn't turn up anything for me on them either. Thank you!
This reminds me of the time when people took a format meant for sprinkling some metadata here and there on a piece of a large document, and started using it primarily for writing "documents" where markup/data ratio is > 2.
I'm of course talking about XML here.
When you're dealing with large amounts of structured data of different kinds that you want to combine in many different ways there's not really anything that comes close to the combination of XML, XPath, schemas, XSLT and XQuery. But if you're not something like the huge medical publisher I worked for then you definitely don't need all that.
I've got an XSLT stylesheet I wrote you can add to an HTML page via the xml-stylesheet directive that turns it into a pretty-printed display of its own markup, complete with syntax highlighting. Pointless but neat that it's possible :)
You can tell many of the current "gurus" have no idea what came before (so they reinvented it poorly).
Development teams go "ok, we need to design a REST API", and, due to the inherent architectural characteristics of REST (large-grained) clashing with their own needs (small-grained) what they end up with is something more like some Frankenstein-esque REST-RPC monster.
Beyond whether its RESTful API or not (grey area) it adds headaches, and is rather unnecessary with good API design anyway, which is what this article was about.
More importantly, compound documents mess with the "cacheability" of resources. A resource cannot declare its own cacheability (which REST suggests) through the uniform interface when its munged into a mismatch of other data. A foo and a bar and a baz are now all cached in together under the same headers with the same /foo?include=bar,baz identifer, meaning that if a bar is likely to change often you have to request the whole thing again. This is one of many reasons compound documents are a pain in the ass, but there are plenty more.
Fastly wrote about all this: https://www.fastly.com/blog/optimise-api-cache-improved-perf...
> The current praise of HTTP/2 as a major win for REST APIs shows how differently "REST" is understood by its proponents today compared to its original meaning. If doing REST today is about doing the opposite of what it was intended to do, it also raises the question why do we believe we get the same benefits REST did for its original definition? A quick read shows we don't, but the cultural ingraining in favor of "believing" is huge.
This whole paragraph makes no sense. I have been making APIs since ~2008 so let's not act like I'm brand new at this.
The point I was making here is that in the past people have moaned about REST because "making lots of requests is slow", which is a common comment from people who generally build their APIs poorly not taking into consideration what the consumers would be doing and just flopped out a very normalized database and forced clients to construct their own data models from that. It's also a concern from people who havent got the first clue what HATEOAS means or how it can help abstract state from multiple clients up into the backend. They just see it as "having to make more requests".
Now that the fear of multiple requests is subsiding, the exact same concepts that made REST beneficial for many APIs in the HTTP/1.x past exist now, they're just quicker in HTTP/2. And they're quicker in HTTP/3. REST and HTTP just get quicker and more useful over time.
Anyway, you sound like you might enjoy this article: https://apisyouwonthate.com/blog/rest-and-hypermedia-in-2019
The link:
Also when 5 becomes 8, 9, 10.... entire APIs come grinding to a halt because people are trying to download the entire database in one single request instead of just making a few calls.
For example,
> Invoices don't need payment status, or an array of order items, so we don't need to worry about trimming down the response at the field level, they can just be their own related resources, discoverable with hypermedia controls.
To me, this misses the mark in two ways.
First, invoices might need payment status, if that's what your UI is trying to render. So, you can certainly have a payment status endpoint, but that's going to add some milliseconds to your UI response times, and if you also have separate API calls for invoice line items, fulfilment status, associated data... well you do have a lot of duplicate I/O going on, even with HTTP/2 and compression, and the API is not really ergonomic from the perspective of the user of the service. There's a lot of client-side LOC in play to make all these calls, and a lot of server-side dependencies end up being exposed by the structure of the APIs.
Second, including the payment status for an invoice is useful and almost (but not always) something that an API consumer is going to request alongside the invoice data. So for the consumer's convenience, you'd typically want to include it in the invoice representation. The API is going to be better off for having it.
In this case, however, we have the problem that obtaining the payment status (or other seemingly simple data) may in fact be expensive. Depending on the architecture of the system, at a minimum it's probably another database query, but the payment status might even be in a completely separate service.
Ultimately, the cost of excess round trips is really paid for by the end-user, but the cost of loading unnecessary/unwanted data is paid for by the service provider. GraphQL, and presumably other sparse fieldset APIs, provide a way to optimise both sides of the API.
This is where GraphQL makes perfect sense to me. It lets you build a discoverable data model that considers the logical structure of the API and the consumer's ergonomics, but because the caller only requests the fields they need, and because GraphQL fields are fetched independently, you don't have to compute those fields unless you need to. The result is super fast APIs that are easy to discover, consume and debug, and that use the optimal level of resources at both ends of the connection, a solution which IMO is infeasible with pure REST APIs distributed over many servers.
Basically, trimming a few bits off of a request is not overall quicker than helping clients not need to make as many requests by designing an API well enough that resources can live on their own and declare their own cachability.
Designing your API resources so that primary data can be fetched and displayed before you start mucking around with secondary and tertiary data is also important. When you talk about being slower for the user, is it actually slower for a real human doing real things on the site. For example, I've seen GraphQL queries loading a social media feed, and list of friends for a little box on the side which didnt matter, and a list of comments on posts that havent even been expanded yet so now user could see it, and querying to see if there is any availability in conference rooms even if theyve not shown any interest in booking a conference room. The clients wanted to avoid making multiple calls because ooooh thats bad, but the whole page was just a blank white screen until all that pointless data came back. Querying all of those resources seperately over the same HTTP/2 connection and letting the UI progressively enhance would absolutely be quicker than mushing it all into a single giant HTTP request but that's what everyone does. All the time. It's insane.
Again, Fastly wrote about all of this very well. This article is one in a series which doesn't stand as well on its own.
https://www.fastly.com/blog/optimise-api-cache-improved-perf...
The problem being that not many people seem to know how to implement it server side, while REST is trivial.
I'm not convinced of this -- or at least, we might use different definitions of "REST".
The guy who created the concept of REST regularly (or used to regularly) call out common problems with "REST" APIs that make them more like RPC APIs that happened to be built on top of HTTP. For instance, [0].
Now, just because someone created a thing doesn't mean they had it right from the get-go. (For instance, the guy who named "gif" clearly pronounces it wrong.) But I've become convinced by Roy's points about what REST ought to be, and if "REST" is going to be meaningfully distinguished from "RPC", it's probably worth understanding his position.
[0] https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
In my admittedly limited experience, GraphQL is easier to get right than true REST, but obviously somewhat harder than a slapped-togther RPC API. I also like GraphQL better because if your domain fits it, it's really easy to use a GraphQL schema as a limited kind of domain model in its own right. And you can factor that schema (as well as describe proposed patches to it) with `extend type`, not unlike C#'s `partial class`.
In the case of most of our partners and clients for instance, the API users are other companies in the same vertical; not only do the API teams not care a lot about the usage as long as it works; spending time doing something 'fashionable in the cutting edge dev world' might be nice, but no-one in their vertical will use it. They will simply ask for the 'normal API' instead.
And if there won't be repercussions -- maybe the product is short-lived, maybe they're okay with forcing clients to upgrade every X months, maybe they rewrite the backend software regularly so cruft can't accumulate so easily -- are they wrong?
GraphQL can't tell you what your problems are. I agree that there could be more resources out there, but as software engineers, we have to wrangle with the specific problem domain we're being asked to solve, and decide what the core issues are and how to tackle them. Nobody outside your team can do that for you (and I count consultants as being on your team, even if temporarily).
Unfortunately the API's I work with are long-lived; decades. Many (but not close to most luckily) integrations are SOAP for instance. With API versioning and 'hidden' APIs (API's which are not in their normal Swagger or docs, but do exist to fix), issues like returning too much or too little is resolved, and it's fast & easy.
While frontend implementations look good, GraphQL sure looks like an unsolved problem on the server to me.
Anecdotally though, I have been working with medium sized startups in NYC that are heavily API driven for the last several years, and I haven't seen graphQL in production once. Not at my companies, nor in any external API we have interfaced with.
I will even go a step farther and day that I cannot recall a single time that another of the several dozens of devs or managers I have worked with has even mentioned the idea of using it.
I can only assume the market OP is referring to is a very specific one. I could make a guess or two, but wouldn't want to ruin the reader's enjoyment from doing the same.
I have integrated many dozens of APIs into backends by now, and never have I even had an option to use GraphQL if I wanted to.
What exactly is behind this statement?
What tools do you have in mind, and what do they do with 400s and 500s? Other than Chrome DevTools coloring the response in red automatically, a big win. /s
I've also never seen an API redirect and force a pointless second (and third etc.) roundtrip for the client to get the same exact content.
Just because (some) of these codes mean something for a web page and a browser, doesn't mean they're automatically meaningful for a real-world API. That's the critical distinctions, which REST fans refuse to acknowledge.
Other clients only treat 5xx as a throwable exception, and some don't throw unless the request fails to hit the HTTP server.
Because of these issues, your best bet is to just wrap your HTTP API in a native client library. You certainly can't target a generic HTTP or REST wrapper.
That said, I think there are very many http clients that implement enough of the standards to me incredibly rich and in line with the spirit of the protocol. The web for humans and machines is based on http. It can improve, but the standard has quite sufficient support to be called practically meaningful
I forget exactly what the scenario was but the request was not being sent with credentials so a 401 was returned, which immediately prompted a retry with the credentials. I'd probably agree that this is covering up an issue that should be fixed.
So in this case you're relying on a specific client's decision more than you're relying on universal interface.