4 ms·
From the linked article: > With all of those caveats noted, HTTP/2 should put to rest any notion of the need to minimise the number of requests for APIs; the p
by whack 7y ago
From the linked article:
> With all of those caveats noted, HTTP/2 should put to rest any notion of the need to minimise the number of requests for APIs; the protocol makes them cheap enough to not practically matter. Go ahead and design a highly granular HTTP API to meet the needs of your clients
This seems highly misleading and missing the point. Sure, you can send a lot of requests in parallel with http/2. But you still incur significant latency if these multiple requests are dependent on one another and serialized. For instance, suppose you have 2 granular APIs that:
- Returns all friends for the specific user
- Returns the name, age and hometown for a specific user
Suppose your client wants to get all of a user's friends whose hometown is NYC. Even with http/2, it has to use the first API, wait for the response containing a list of userIDs, then use the second API to get all of their hometowns.
Whereas with GraphQL, it facilitates building a single API call that says "give me all friends for a specific user, along with each friend's hometown". Now, there is only one round-trip latency cost. Not two. Even with the overhead improvements mentioned, the cost of two back-to-back round-trips would be much higher. It is very odd that this isn't mentioned in the article or any of the other articles they reference.
- JulianMorrison 7y agoEven directly accessing a database on localhost, your code will be slow if you send "select id from user where user.friend = ?" and then 1000 times "select town from address where user_id = ?" rather than one query with a join.
- jlokier 7y ago> Suppose your client wants to get all of a user's friends whose hometown is NYC. Even with http/2, it has to use the first API, wait for the response containing a list of userIDs, then use the second API to get all of their hometowns. Not only that, but the resulting set will have inconsistencies if the database changes between the two requests. It's very unusual for HTTP applications to maintain transactional consistency over multiple requests. This can be a security issue among other things, and even when it isn't, it can result in the display of glitchy data, whose inconsistency persists in the client which fetched them. For example, you get all of the user's friends' userIDs. Then the DB is changed to remove one of the friends (by that friend). Then that friend changes their hometown to a new place, that they don't want the original user to know, and expects the original user can't see. Then you do the second API call to get the hometowns of the userIDs. The result contains the new place that you should not have been able to access, and maybe it's shown on a screen for an unbounded time after that, or printed in a report. Similar issues occur when fetching data to sum into totals and finding it doesn't match an expected total, or expecting some logical constraint to hold for every item in the result because it's implied by the original query. (Live-streamed query updates turn these into momentary glitches, which is still wrong but at least those disappear from view soon after.)
- traek 7y ago> Then you do the second API call to get the hometowns of the userIDs. The result contains the new place that you should not have been able to access This is an issue with access control, not HTTP requests. The second HTTP request shouldn't return information you don't have access to.
- jlokier 7y agoNot really, that's just an artifact of the example, which I constructed to emphasise that consistency errors can be serious. But I agree that if access control were used (and pushed down to the database, rather than implemented on the client side), it would prevent that particular example from returning the disallowed data. Even with access control the problem is still there. In the example but with database-level access control added as you suggest, the client gets an access error instead of a consistent resultset. Or missing data instead of a consistent resultset. In either case, the client needs extra code to handle the inconsistency caused by the sequence of dependent HTTP requests, or it will render an invalid screen. In some cases, maybe throw errors trying to render it because the data is in an "impossible" state that wasn't allowed by the database logic. For example, a person without an address is shown, even though the database never contains a person without an address. Or nothing is shown, because of the access error, unless the client has "retry on access error" logic. Try to imagine how unprofessional that would look if it was, say, a bank statement line without an amount. These inconsistent data hazards can get complicated easily, as they arise from all sorts of weird corner cases that rarely arise and are hard to enumerate. Defensive programming is needed to handle them, and even then it can be hard to ensure every case is handled well. They are also hard to test.
- jiofih 7y agoI think the Fields: header in the library is supposed to be used for that, it would look something like Fields: /users/*/hometown in the same request. It feels like it’s solving all the wrong problems to me - who actually wants to put varnish in between GraphQL client and server? Caching can be done more efficiently at either end. And this doesn’t provide a replacement for a query language, just moves some of the optimizations to the transport (http2) layer. Now I’m wondering why Apollo doesn’t use http2 push to batch requests, instead of merging queries. Or does it?
- imtringued 7y agoNow all they need to do is allow the fields header to represent arbitrary graph structures and they will have reinvented GraphQL poorly.
- MapleWalnut 7y agoWhy wouldn’t you return more data for the REST API instead of making dependent requests?
- ppseafield 7y agoAt my previous job we had a method that became known as a "superset". It would fetch a record and all data related to that, and then a lot of other data related to that. It would return tens of MB of XML every time. It was huge, but so many services came to depend on it because it always had that piece of data someone needed. All the data you needed was there, but every request caused a lot of memory usage and lots of database queries. GraphQL allows you to just ask for the fields you need without necessarily incurring the cost of returning anything else.
- g_delgado14 7y agoBecause then 2 months later a dev refactors the front end to only require one of the fields, and suddenly you have the gorilla holding a banana and the whole jungle when all you asked for was a banana.
- tedunangst 7y agoYeah, it'd sure be nice if there was some way for the client to specify what data it wants.
- ldiracdelta 7y agoI do that for my backends. On the HTTP request, the client sends a `?fields=encodeURIComponent(<JSON>)` query parameter to the server. If `fields` is present, then the API sends back the pruned version of the REST API. If `fields` is not present, then all the fields are sent back. Alternatively, you can prune the fields to a subset by default. The <JSON> looks like `{ foo=true, bar={baz=true}, bap=true} ` to imply the endpoint should send down field `foo`, nested object `bar` with only its field `baz` and the entire default nested object `bap`. I achieve this in Django Rest Framework by using meta-classes.
- onion2k 7y ago
- tensor 7y agoThis also ignores things like Hasura which does global optimization by converted GraphQL into a single SQL query.
- splintercell 7y agoRecently tried Hasura, big fan.
- JoshTriplett 7y ago> Whereas with GraphQL, it facilitates building a single API call that says "give me all friends for a specific user, along with each friend's hometown". Better yet, submit a request asking for all friends whose hometown is NYC (or whose hometown is the same as one of the user's previous hometowns, or...). That way only a subset of the data needs transmitting.
- derision 7y agoIt's not really a valid argument, because if you only have two APIs and you can't create more, you can't switch to a GraphQL implementation in less time than you could add an appropriate endpoint. GET users/<id>/friends ?fields=friend.* &where=hometown:'NYC' &aggregate=distinct
- querulous 7y agohttp2 has push, so you can make a single request (for the friends of a user) and then just push all the users in that response before the client requests it. this all happens over a single connection and leverages all the http infrastructure and experience you already have