9 ms·
Let's stop building APIs around a network hack (2017)
- andrei 6y agoShould add the date to the title: (2017)
- peteretep 6y agoI wish anything built on REST would die. Conflation of the envelope and the payload is idiotic and adds only pain, no value.
- psturgeon 5y agoThat's a constructive comment based on a well rounded understanding of the various architectures and paradigms around. You sound well balanced and like you're a pleasure to work with.
- CBLT 6y agoI've gotten value from HTTP responses in the 300s, 400s, and 500s being handled appropriately by various tools without having to write special RPC logic for those cases. What am I missing?
- peteretep 6y agoThat a 404, 301, and 500 being generated by the API vs the web server are very different events and it’s hard to think of where “various tools” handling them the same is the right choice? What examples do you have where it’s the right choice?
- CBLT 6y agoWith application servers acting as the authoritative state of a user's current phonecall status, the api client wants a websocket connection to the shard with that user's state. It connects to one and is 302'd to the appropriate shard. It how has a websocket connection directly to the authoritative source to get notified of any state changes. 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.
- peteretep 6y agoHow is this a REST API rather than just a regular API being served over HTTP?
- slver 6y agoCuriously the websocket spec says the server may send 30x code during handshake, but the client is "not required to follow it". So in this case you're relying on a specific client's decision more than you're relying on universal interface.
- smt88 6y agoHTTP and REST are never reliably supported by clients. For example, many JavaScript clients will throw exceptions for 4xx and 5xx errors, even though handling 4xx errors in the client application is very reasonable. 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.
- jbothma 6y agoThat's a reflection on a small number of Yet Another Not Invented Here clients, as opposed to the protocol and paradigm
- smt88 6y agoYes, I agree it is not a reflection of the protocols. It doesn't matter either way -- if you have a standard that no one fully implements, your standard is not practically meaningful.
- jbothma 6y agoI think partially implemented standards are very useful! You can partially implement in a way that provides escape hatches for users, or you can lock them in to your assumption of the right behaviour. 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
- deleted 6y ago[deleted]
- slver 6y ago> I've gotten value from HTTP responses in the 300s, 400s, and 500s being handled appropriately by various tools without having to write special RPC logic for those cases. What am I missing? 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.
- jorge-fundido 6y agoMiddleware comes to mind (eg caches, proxies, api gateways).
- pjmlp 6y agoI wish rebuilding RPC infrastructure every couple of years would die, I already lost count of how many times since DCE RPC was cool.
- slver 6y agoUnlike everyone improvising on "what it means to be REST", no one is shoving RPC down your throat.
- JimDabell 6y agoREST is more or less a description of the architectural design principles that made the web a success. You wish for the web to die?
- slver 6y agoFielding has no problem recognizing the web needs one architectural style, and small-grained data APIs need another. The web doesn't need to "die". Our infinite ignorance on what REST is and what it should be used for has to die. Fielding hasn't mentioned the word "API" once in his paper. This entire REST API movement is a paradox that mocks everything he explicitly warned us about in his dissertation.
- watermelon0 6y agoI think there is still latency to consider, especially if you have users around the world. 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.
- anonymoushn 6y agoThis is especially true since HTTP/2 uses TCP, and TCP is designed to interpret flaky wifi or cellular connections as congestion and become unusably slow. It's a fairly common situation in which you don't want to pay for additional round trips.
- cma 6y agoIsn't that handled with layer 2 retransmissions now?
- yaantc 6y agoPartially. There are local (L2) and fast re-transmit with ARQ [1] and HARQ [2] (at least in cellular, which I know, but I guess Wifi likely has both too), and it can help hide some packet loss to the upper layers. But this still creates added delay, which is seen by TCP as more RTT jitter, with a bad impact on performance. Also, if the link degrades really badly TCP will definitely see more congestion and loss. 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 https://en.wikipedia.org/wiki/Automatic_repeat_request [2] https://en.wikipedia.org/wiki/Hybrid_automatic_repeat_request https://en.wikipedia.org/wiki/Hybrid_automatic_repeat_reques...
- Twisol 6y ago
- 1cvmask 6y agoThe forum link on the site is broken. Assume everything is on their Slack channel now. The link: https://forum.apisyouwonthate.com/ https://forum.apisyouwonthate.com/
- sfvisser 6y agoLots of talk why you don’t really need to have compound documents anymore, but no real arguments why compound documents are bad. If your server and client architectures are somewhat sane adding a compound on-load resource is cheap and I don’t see many downsides. Maybe even pack them into your initial HTML.
- psturgeon 5y agoThere's loads of problems and I've been talking about them all through a series of blog posts and talks, I just didn't cover it here. 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 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.
- thinkingkong 6y ago4 years later and Id argue graphql is the clear market winner. Whether or not you really need it is debatable but its fashionable. Schema driven development and types have also been adopted along side that system so they sort of fit well together, too.
- smt88 6y agoI have almost never encountered GraphQL in production, including when inspecting other sites. I think it's still a pretty small niche and mostly used on newer projects.
- xcambar 6y agoSame for me. I will add to this that probably very few know how to implement GraphQL on the server-side *efficiently*, or, put differently, most have a gut feeling about how hard to manage it can become. While frontend implementations look good, GraphQL sure looks like an unsolved problem on the server to me.
- smt88 6y agoI don't use it only because people at other companies demand REST.
- tluyben2 6y agoI was going to comment something similar to this. We integrate with a lot of APIs every day and graphql we have never encountered. It seems, for now, to be a HN distortion field that it is 'a clear market winner'? It seems not only to be used on newer projects but at a specific type of startup projects. Looking at HN launches which have an API recently, it seems hardly used at all yet. The problem being that not many people seem to know how to implement it server side, while REST is trivial.
- Twisol 6y ago> 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-hypertext-driven https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
- skrebbel 6y agoIs the author somehow implicitly assuming that APIs are only to be used between microservices in the same data center or something? 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.
- psturgeon 5y agoHi, I am not assuming all microservices are in the same data center. 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-performance https://www.fastly.com/blog/optimise-api-cache-improved-perf...
- 83457 6y agoThe author specifically talks about browser support so I guess not. Given they are talking from the context of a large modern company (however you want to put that) I doubt they have to worry about users communicating with a server on the other side of the world.
- psturgeon 5y ago
- Animats 6y ago"HTTP/1.1 actually solved this with Connection: keep-alive, but not many people seem to use that. 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.
- tgv 6y agoIdk. It requires server side resources to keep the connection open, isn't it? I think nginx keeps them alive by default up to a certain limit.
- brabel 6y agoThis is wrong. HTTP/1.0 had a keep-alive header field as mentioned in the HTTP/1.1 RFC: https://tools.ietf.org/html/rfc7230#appendix-A.1.2 https://tools.ietf.org/html/rfc7230#appendix-A.1.2 But in HTTP/1.1 that's the default: https://tools.ietf.org/html/rfc7230#section-6.3 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" mechanism
- psturgeon 5y agoDid you read the link you shared? 19.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.
- skhameneh 6y agoRelating to the article, there is an (unfortunate) ongoing discussion around removing server push from Chrome/Blink/Chromium: https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ?pli=1 https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYL...
- slver 6y agoYup, push is basically dead.
- toomim 6y agoI don't think so. Chromium walked back their plans to deprecate it, and it's not leaving the IETF RFC for HTTP/2, and lots of people still like it.
- mort96 6y agoHuh, that seems weird to me. _Obviously_ push would go almost nowhere in 5 years. Hell, HTTP/2 itself is still extremely new. Server push requires people to totally redesign how their infrastructure works, because their web server needs knowledge about the application. 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.
- marctrem 6y agoI would have liked the author to have acknowledged dataloader in the context of graphql.
- slver 6y agoDataloader is an implementation quirk of a particular library, not a protocol quirk of GraphQL as a query format.
- slver 6y agoThe author assumes serving documents (i.e. compound data) is not RESTful and a "hack". 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.
- deleted 5y ago[deleted]
- psturgeon 5y agoIf you make your quotes small enough you can make them mean anything, but I invite you to go take a look at the dissertation. "large-grained resources" does not mean "compound documents", the REST dissertation has no comment on compound documents or anything that resembles the concept, which came along later. It doesn't forbid them or allow them, but many of the concepts of REST suggest otherwise. 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-performance 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 https://apisyouwonthate.com/blog/rest-and-hypermedia-in-2019
- bni 6y agoCompound documents are not only for reducing network latency. It also about giving your clients a nice single point of entry and not having them call 5 endpoints to get what they need.
- psturgeon 5y agoIt sounds nice at first, but if you have 5 completely different resources then why would you want to squish them into one? This taints highly cacheable data with frequently changing data, and slows down everything all at the same time. 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.
- bullen 6y agoThis article is a bit misguided, and contains multiple fundamental missunderstandings, first no history: 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. http://root.rupy.se http://root.rupy.se
- ncrmro 6y agoI’m pretty sure you just described GraphQL. All errors are 200 unless they are HTTP errors. The backend error is under the errors key in the json. All post requests.
- mort96 6y agoHe just described, well, most web APIs. Client POSTs to some endpoint, the server responds with 200 and a payload. Application-level errors are part of the payload. 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.
- psturgeon 5y agoIgnoring all the layers of HTTP to use it as a dumb tunnel is a really effective way of forcing everyone to reinvent a lot of wheels at every phase of the interaction. A lot of people do it, its usually a brittle and expensive waste of time.
- speedgoose 6y agoGraphql is much nicer to use than the suggested solution to do one billion http requests for the sake of it.
- psturgeon 5y agoGraphQL, OData, and JSON:API some of many compound document implementations you can use to mush multiple resources into a single request, this article was about how doing that is pointless and sometimes detrimental.
- doctor_eval 6y agoMaybe I'm a bit stupid but I don't understand this premise of this article. 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.
- psturgeon 5y agoI covered all of that in a previous article and many more since. https://apisyouwonthate.com/blog/graphql-vs-rest-caching https://apisyouwonthate.com/blog/graphql-vs-rest-caching 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-performance https://www.fastly.com/blog/optimise-api-cache-improved-perf...
- SergeAx 6y agoI am actually a tad concerned that this article made it past 100 upvotes. It was my own treshold for giving an entry some attention, and I think it is time to give it a bump. I used to regard HN as collaborative filter for a wide stream of tech news, announces, tutorials etc, but recently an average quality of that filtered data became a bit disappointing. Or maybe it's just my own standard got higher?