[HN Gopher] GraphQL vs. REST APIs: a complete guide ___________________________________________________________________ GraphQL vs. REST APIs: a complete guide Author : makaimc Score : 63 points Date : 2023-03-03 20:09 UTC (2 hours ago) (HTM) web link (www.airplane.dev) (TXT) w3m dump (www.airplane.dev) | chrsjxn wrote: | The article talks about how GraphQL is "flexible" a lot, but I | kind of wish they'd spend more words talking about why that | matters. | | Eg, if you're working on a new product, the odds are very good | that your front ends will change pretty rapidly. At the very | least, we're way more likely to A/B test application features | than we are API schemas, regardless of what methodology or | technology our APIs are using. | | Because that's why I would pick GraphQL. It's a choice that | reduces the barrier to rapid iteration in the clients, but it | does add backend and infra work to support it. | blagie wrote: | I don't like GraphQL due to the complexity. It's not JSON or | YAML, and requires a lot of custom stuff on both ends. | | I'd like something like GraphQL, but built on standard data | formats. | braindead_in wrote: | REST is basically a subset of GraphQL. | eterps wrote: | From a caching perspective REST is a superset IMHO. | TurningCanadian wrote: | There's a lot to caching that REST gets wrong. It thinks in | terms of URLs being cacheable, but that's very limiting. | GraphQL caches at the level of the objects in the response. | Almost every REST API will return multiple objects from one | URL. With GraphQL, newer data coming in from query2 can | update components showing data from query1 if they both | reference the same object even if query2 is a different URL. | REST just doesn't have those smarts. Yes, a HTTP DELETE | request will clear the cache for a particular URL, and | there's no equivalent in GraphQL, (that I know of) but I | don't think I've ever been able to take advantage of that in | real life. | | At the network level, GraphQL can automatically set cache | headers depending on the content. REST doesn't know what | you're sending, so it's up to you to know that when you start | sending mixed data (to save clients a roundtrip), the cache | header may be impacted. | https://www.apollographql.com/docs/apollo- | server/performance... | | On the client-side, caching with GraphQL is far ahead of | dealing with a REST API. It knows more about what data it has | in cache so it's better able to automatically avoid network | requests. Further, a cache-and-network fetch policy is just | another parameter for your query and the plumbing for that is | handled transparently. | denysonique wrote: | For JavaScript based projects TeleFunc[1] can make development | way simpler by 'removing' the need for an API. | | [1]: https://telefunc.com/ | recursivedoubts wrote: | imma let u finish, but a hypermedia is a necessary constraint for | an API to be RESTful... | | edit: more usefully, you need to be really careful w/ your | GraphQL end points because they are being access in an untrusted | computing environment: | | https://intercoolerjs.org/2016/02/17/api-churn-vs-security.h... | | my understanding is that facebook whitelists their graphql | queries but, last I looked, I didn't see security getting much | attention around it... | jensneuse wrote: | Here's how you do it: | | - REST/Hypermedia when you're actually building a website (not | app) and your "site" is a state machine | | - OpenAPI when you expose and API to 3rd parties | | - Isomorphic TypeScript APIs when you're using TS on both backend | and frontend [0] | | - GraphQL when the frontend needs to talk to an API layer | provided by different teams | | - OpenAPI when one backend talks to another backend | | - gRPC might be an option, but most teams don't need it | | [0]: | https://wundergraph.com/blog/isomorphic_typescript_apis_end_... | [deleted] | timr wrote: | Simplify that to: | | - REST by default | | - everything else might be an option, but you need to justify | it with specific reasons. | | Also, having a JS-driven front end or multiple teams is not an | automatic justification for the complexity of GraphQL. | | GraphQL is a _huge_ burden, for very little practical gain. | Most companies have very small number of examples of conflict | areas where queries need to be optimized, and for the most | part, these can be resolved through smarter front-end | engineering (like not throwing React at CRUD websites). | | It's amazing how we managed to build large-scale websites with | essentially all modern functionality without all of this stuff | for years and years, and only since 2012 or so have these tools | become "necessary" (and I'm being generous with 2012...it's | more recent than that). | chiefalchemist wrote: | I haven't used it a lot but one of the benefits of GraphQL | that caught my eye is you get to specify what you want | returned. So, for example, instead of 20 product objects (all | available properties) you can get 20 products with name and | price. | | Also, as I understand it, when implemented properly, you can | get more complex (?) data back. For example, instead of | getting 10 orders, and then 10 more requests for the items | for each order, you can make a single request and get both in | a single response. | | I believe both concepts apply to creates / updates as well. | timr wrote: | > but one of the benefits of GraphQL that caught my eye is | you get to specify what you want returned. So, for example, | instead of 20 product objects (all available properties) | you can get 20 products with name and price. | | Yes. But you have to _implement_ this (simple in this case, | but not so simple once people go hog-wild with the GQL). | Also, I don 't think I've ever had a situation in my career | where loading name _and_ price is a significant burden over | either field individually. On the other hand, it 's a huge | burden to have to implement the back-end system that | supports a fully orthogonal, structured query language for | the UI. | | To be fully charitable to the argument you're making, | probably the real reason people do this is because they | want to decouple development between Team UI and Team | Product and Team Pricing, and it's just "easier" to expose | "web SQL" to Team UI and let them go nuts, rather than | sitting down with them an exposing targeted endpoints that | give them exactly what they need. | | ...but now you have N problems. Your web app has become an | ad-hoc distributed query language executor, and you have to | think about the semantics and load implications of all of | that. | mattmanser wrote: | That's not an advantage, from the DB side the two queries | are indistinguishable because they use the same indexes. | They will take virtually the same CPU to complete. | | If your user has authorization to access this extra data, | the actual cost of sending those extra fields properties | over the wire is ridiculously trivial. | | Think a kilobyte or two, if not a few bytes. | | Any justification you think you have for just sending the | name + price is utter bullshit. You would save vastly more | in bandwidth by not sending whatever dumb graphql code you | wrote as part of your front-end build. | | That is, unless you are Facebook or Google and have already | optimized the hell out of your front-end libraries. | | Which I guarantee you aren't and haven't done. | jensneuse wrote: | I more or less agree, but recent advances in tooling has | changed this. E.g we've built a GraphQL to JSON RPC compiler | [0], so we're hiding GraphQL behind a JSON RPC wall which | also enables powerful code generation. We're using this | successfully to build our own cloud [1] using our own tooling | and I quite like it. | | Shameless plug, I'd love to get feedback if you have the time | to try it out. | | [0]: https://docs.wundergraph.com/docs/features/graphql-to- | json-r... | | [1]: https://cloud.wundergraph.com | timr wrote: | I mean...ok, but...wow. You built a _compiler??_ For | writing a _web app?_ And you 're not Facebook? | | Look, maybe this is totally appropriate for your | application domain. I don't know. But this kind of "power" | is immensely complex. We have to be honest about the costs. | | I have to keep reminding people that essentially _all | functionality of the modern web existed prior to 2012._ | IceDane wrote: | How is GQL a "huge burden"? Would you care to elaborate? | timr wrote: | I could write a book on this, but just a few: | | * your teams will now spend a big portion of their time | thinking about GQL primitives and mapping and syntax (oh | my) and otherwise caring for and feeding this beast. | | * you still haven't solved the underlying problem -- you | still have to figure out why your GQL requests are | hammering the back-end(s...let's not forget that a lot of | this stuff came from poor decision-making re: | microservices!), only now it's indirected and harder to | optimize. | | * ...oops, you forgot to implement all the corner cases of | your GQL schema! Go back to 1 and start over. | | Say what you will about REST, but the syntax is dead | simple, it takes about 15 minutes to learn, and the | semantics are crystal clear. Whatever complexity remains is | actual complexity in your problem or org structure, and you | should _probably_ just fix that instead of trying to find | magical tech beans to abstract the problem away. | IceDane wrote: | > * your teams will now spend a big portion of their time | thinking about GQL primitives and mapping and syntax (oh | my) and otherwise caring for and feeding this beast. | | What exactly do you mean by this? My team spends exactly | zero time "thinking about GQL primitives and mapping and | syntax"? What programmers have a problem understanding | GQL syntax? I have explained GQL and set people to work | in a 30 minute session. I've even had non-technical | people write automated tests for APIs. If they can't | write the queries themselves, they can simply use any of | the great query builders(like apollo studio or what have | you), where they can quite literally explore the entire | schema, with documentation, and point-and-click their way | through building any query. | | > * you still haven't solved the underlying problem -- | you still have to figure out why your GQL requests are | hammering the back-end(s...let's not forget that a lot of | this stuff came from poor decision-making re: | microservices!), only now it's indirected and harder to | optimize. | | How is this really any easier with RESTish APIs? In the | end, this boils down to how you are doing observability. | If you don't do any observability, you are shit out of | luck whether you use GQL or REST. With GQL, you can quite | literally automatically instrument your entire schema and | get detailed tracing information for your entire graph. I | have sentry set up to trace all our requests from our | frontend, to our GQL gateway, to the subgraphs, to other | services they call out to and even time taken doing | database queries. It was a few lines of code. | | > * ...oops, you forgot to implement all the corner cases | of your GQL schema! Go back to 1 and start over. | | Corner cases? If you have some underlying data you need | to expose, the difficulty of exposing this data in the | most appropriate manner comes down to the complexity of | your data, and whether you use GQL or REST to expose it | doesn't really change it. With GQL, types and relations | are at least "native" - they are the source of truth. | With REST and something like OpenAPI, you'll constantly | be fighting limitations and have to trawl through | hundreds of endpoints, some of which might be doing extra | stuff for the sake of ease of consumers, etc. Feel free | to elaborate on what sort of corner cases you have in | mind. This sort of vague hand-waving isn't really useful. | dmitriid wrote: | GQL, in no particular order: | | - GQL allows unbounded arbitrary queries. The awkward | workarounds turn it into REST with none of REST semantics | | - default query method (POST) cannot be cached by literally | anything in the infrastructure because POST is not | cacheable by definition. The awkward workarounds require | you to parse and look up fields in both request and | response. Both of them arbitrarily large. | | - instead of delegating requests to optimized queries (db | or services) you now collect all that data manually, in- | memory, on the server with little to no insight into what | the data is, and how to optimize its retrieval | | - default types doesn't provide useful primitives like | dates | | - you now have to keep up with evolution of every single | microservices you depend on | | There's more that I've forgotten. | | It's really good for building internal tools that usually | have different requirements than what existing APIs provide | you | flashgordon wrote: | One thing Id suggest - "gRPC might be an option but most teams | dont need it" - needs a revisit. As a backend-dev (who does | front end when needed - and that too by slapping NextJS on an | API) I have found starting from a grpc set of services and | generating http/openapi specs from it to be much for | maintainable and understandable than starting with the OpenAPI | spec and working "inwards". Apart from a (mostly) single source | of truth for my interface intent - I also get a system that is | amenable to scalability concerns than most of the time when I | started working inwards. I might be in the minority as most | folks who build services come from a product background? | jensneuse wrote: | If we compare gRPC to OpenAPI, we get the following. gRPC | will be faster at the tradeoff of complexity. You need more | tooling, you cannot easily inspect binary traffic, you cannot | simply call gRPC from the web, you need a grpc web proxy, | proxies and API gateways are more complex for gRPC than for | an OpenAPI/REST-ish API, and the list goes on. I can put my | protobuf in a registry, but the same applies to OpenAPI. So | what really is the difference? And do we really need the gRPC | level of performance with this hefty price of complexity? I | doubt it. Most of the time, your database will be much slower | than what gRPC might give you in performance compared to | OpenAPI. | | Sorry if I'm pushing back on this. It's not against you. I've | just seen way too many devs and teams who created gRPC | "micro" services and all they needed was one boring well | designed OpenAPI monolith with good architecture. | | gRPC is great if you really really have the problems it tries | to solve. I believe most companies have a much bigger problem | than performance, the inability to design APIs. You cannot | solve bad API design by using a faster tool. Fix your API | design, OpenAPI is great. | random3 wrote: | Here's a bunch of random points (this deserves a full blog | post) | | - it's not about performance | | - OpenAPI needs and has tooling (it just doesn't work) - | think about documentation, client/server code and evolution | support for forward/backward compatibility, type safety, | etc. | | - you can build monoliths and microservices with both | | - gRPC / protobuf gives you a pragmatic separation of | concerns (and typing among other things) | | - gRPC allows exposing REST/OpenAPI with defualt tooling | and it works | | - there's an extensive set of best practices that combine | the best of both worlds - just read https://google.aip.dev/ | | - if the workd prefers GraphQL, you're out of luck | | - gRPC / protobuf help you focus on the actual contract and | make everything else (that's 90% of time) a non-problem. | | --- | | Disclaimer: I'm in the grpc (and protobuf) camp and have | built APIs with REST, Thrift and gRPC/Protobuf for more | than 15 years (gRPC mostly last 5 years) As an | infrastructure startup we have to both expose and consume | both REST and gRPC. | | If you build APIs and decide to handle the above manually - | you must feel productive doing work that compilers and code | automation tools have been invented for. However tooling | should automate it for you. | flashgordon wrote: | Pushback is always good. I am not buying the argument (from | having done large scale systems in both) that OpenAPI is | significantly more productive than grpc - especially when | building SaaS/control plane services. I do agree tooling is | "prettier" for OpenAPI but that is artifact of needing that | level of tooling for a spec that to me never felt as | expressive to begin with (have to be careful not to get | into Yaml vs syntax wars her). On the contrary | reading/modifying/consuming grpc specs have never needed a | custom UI. OpenAPI is way too tied in the hip with http | (doesnt need to be) and a lot of things evolved that way. | Grpc never had this problem. As you may have pointed out it | needs an extra grpc-gateway plugin to generate a http | gateway (which I actually is neat) | | +1 on the terrible state of grpc cli tooling (unless you | are already working at a faang). | | -1 on Web proxy - As mentioned before I actually find that | seperating a http "transaltion" gateway from your core biz | logic is a win in my mind. | | +1 On _way too many_ microservices. IMO whether you use | GRPC or OA, you can build monoliths or way too many | microservices. I have not felt GRPC explicitly enforced | that. What I _did_ get from grpc is the logical seperation | with grpc (you can still bundle multiple services in a | single binary vs as seperate microservices) if you want. | With grpc (nay proto spec) building a service becomes a | logical exercise over how do i structure URLs vs what verbs | to use etc. | | PS: On a completely diff note I actually like your approach | to isomorphic types. My switching to the grpc side was | largely motivated by this single source of truth from | whence all layer specific types came forth from! | lazyasciiart wrote: | Why does OpenAPI appear to be an alternative to REST in your | list? | jensneuse wrote: | I understand REST APIs more like Hypermedia APIs, where you | go to the landing page, it contains links, you follow them, | you leverage content types, etc... So by definition, a REST | API has no specification, it's an API that the agent can | explore. | | In contrast, an OpenAPI is a REST-ish API with an OpenAPI | specification. It's not as dynamic as REST, it's not | Hypermedia, it's much more "JSON over HTTP" but also not | exactly RPC. | shagie wrote: | Building on this... | | Look at https://api.github.com/users/octocat | | Or the OpenID Connect and /.well-known/openid-configuration | https://openid.net/specs/openid-connect-discovery-1_0.html | | And while I might be invoking scotsmen, very few things | look like https://www.ics.uci.edu/~fielding/pubs/dissertati | on/rest_arc... and so are best described as "REST like" or | "RESTish". | senda wrote: | Should graphql ever be used to just cover all the basis of | system integration? | meowtimemania wrote: | The huge thing I love with graphql (and this probably works | with OpenAPI as well) is run time mock generation. It makes | writing tests a breeze and semi enjoyable. | | example: https://www.freecodecamp.org/news/a-new-approach-to- | mocking-... | ElijahLynn wrote: | Article could be improved by mentioning that the smaller rest | requests will have a much higher cache hit ratio when used with | HTTP/2. | gtramont wrote: | I take not mentioning hypermedia in a post discussing REST as a | huge red flag. The author mentions Roy Fielding's dissertation, | but apparenly they didn't read it thoroughly. | | Furthermore, I believe that a huge confusion comes from not | understanding what a "resource" is: it is not a database table*; | it is not your domain model*; it is a _representation of state_. | Like this very orange page you're looking at. This whole page is | the resource identified by | https://news.ycombinator.com/item?id=35014395. Just like the | stylesheet for this website is a resource identified by | https://news.ycombinator.com/news.css. | | * It may be, if that's the _representation_ you want. | ynab4 wrote: | [dead] | twodave wrote: | I like GraphQL because it allows me to describe my API like a | domain rather than just a collection of resources. REST is | difficult to map to a true domain model because domains aren't | just collections of resources. They have nuance. And with GraphQL | your API matches your domain, so there's no limit to what your | application can accomplish. For example, adding another feature | to your front-end no longer requires going back to your API to | add a tailored endpoint nearly as often. At worst you're adding a | property somewhere that is now available to all your use cases. | GraphQL embraces mutations in a way that REST frankly can't | without being glorified RPC, and it doesn't have the same | perverse incentives such as re-using an endpoint because it | exists even though it may be suboptimal. | | So, in my opinion, unless your application is very | resource/entity-intensive (and you don't mind a little RPC-style | bloat) then GraphQL is often a better choice than REST. And if | you actually have a rich domain to model (this is the case even | for many small applications), then GraphQL is often a better | choice than RPC. | gofreddygo wrote: | I love graphql. It allowed me to scrape a whole bunch of | websites with almost no changes to my scraper. | berkle4455 wrote: | graphql is a data exfiltration engine, it's lovely | WirelessGigabit wrote: | I don't like that GraphQL is called GraphQL. | | Its output cannot represent a graph. Its output is finite. | | Consider an API that queries people. You query a married person | and include their spouse. And then you include the spouse's | spouse, and so on. | | GraphQL will render as many levels as you request without | deduplication. | | JSONAPI will do this much better, you'll get 2 entities in | 'data', and they'll refer to each other in their relationships | block. | | Not that I like JSONAPI. Having built larger system with it there | is no universal way of supporting pagination in the includes | section which makes it a binary proposition about includes. You | get ALL the ones you request, or none. | foota wrote: | It's a query language for graphs, not a language for | transferring graph data. | IceDane wrote: | There seem to be a lot of people pushing back on GraphQL who | often don't seem to understand what it really is, or what it can | do. They often seem to think that it's super complicated to build | and use, and that REST(-ish) APIs are somehow magically simpler | and easier. | | Are RESTish APIs simple to build? I mean, yeah. You can have a | server running serving an endpoint in like a minute in most | programming languages. But FWIW, this also applies to GQL. Take a | gander at any GQL resolver tutorial. But is that really what | makes it simple? | | When you build APIs, they aren't made to exist in isolation. | They're made to be consumed. Ideally, they should be easy to | consume, which can mean a lot of things.. but "it's easy to send | an HTTP request" is not one of those things. | | They're easy to consume if they are documented and come with | schemas. They are easy to consume if there is tooling to generate | fully typed clients for them. Arguably, OpenAPI schemas could fit | this description, but in my experience, OAPI support is really | varied, both on the server and for clients. Generated clients | tend to be _lots_ of code and incredibly clunky. Support server- | side is heavily reliant on the server libraries being capable of | exposing schemas in some way. In some languages this is trivial, | but in e.g. javascript or typescript, you will need some way to | expose your types. Just having typescript interfaces isn 't | enough(without extra tooling or reflection). | | GraphQL is typed by definition. The schema _is_ the API, and the | types, and the documentation. We have great tooling to generate | code in any language, and code-first resolver libraries(like the | excellent pothos-graphql.dev) make building your schema along | with your resolvers an absolute joy. | | Then there's things like the N+1 problem. RESTish APIs are sort | of "procedural". Each endpoint is usually a resource, and if you | want to fetch a resource and its related resources, you will | often have to fetch it and them separately. You can also embed | them in the resource, or you can resort to all sorts of not-very- | standardized methods of conditionally expanding relationships to | do it, which is already just sort of doing what GQL does, except | much worse(and is I think not supported in OpenAPI at all?). In | GQL, you can also run into N+1, but modern libraries make this | super easy to solve. Either they can tightly integrate with | something like the underlying ORM, or you can write dataloaders | for your _types_ , so that every time a specific resource is | requested en-masse, it is always loaded efficiently. Not so much | for REST, unless you apply very careful engineering and design. | | GraphQL just makes all these things such a breeze. There are some | things you need to think about, of course. Query complexity is | one thing; you don't want someone to bring down your server using | 30-level nested query. Thankfully, people have been using GQL for | years at this point and most of these problems are solved, and | many of them incredibly neatly. | [deleted] | coolgoose wrote: | Well, seems somebody doesn't care about jsonapi/openapi and the | rest of the family. | | Sparse field sets / compound documents are nothing new and I'd | argue its easy to overlook that there's no magic in the backend | for graphql either. | zoover2020 wrote: | I think OP didn't make the distinction between RESt and RESTful | APIs. A pity IG you'd ask me | doctoboggan wrote: | I've been writing flask apps for a while (using sqlalchemy as my | ORM) and find myself repeating code all the time when I create | GET and POST end points for all my models. | | Is there a better, more automatic way to generate GET and POST | endpoints automatically when I create a new model? | revskill wrote: | Just like OOP vs FP. REST is OOP (you start with resource), | Graphql deals with function (query and mutation). | yamtaddle wrote: | I'm not following how shifting the resource definition from | server-side to client-side makes it any more "FP" than REST. | k__ wrote: | Where does tRPC, where you start with types, fit in? | revskill wrote: | Yeah, it would be like dynamic type vs static type. | satvikpendem wrote: | GraphQL is extremely useful, if only to have strong type | validation between disparate languages, via code generation. It | can be a universal typed schema of your data against which you | can use whatever server and client combo you want. It's like tRPC | but you're not forced to use only TypeScript. | | I also have to plug this video [0], since many people think | GraphQL somehow is a competitor to SQL and that it goes between | the server and the database; it does not and has not ever been | intended to be a replacement for a database query language and in | fact should never be used there at all. It is only for | client/server communication, not server/database communication. | | [0] https://www.youtube.com/watch?v=ZfccwYUD8H0 | [deleted] | hbrn wrote: | I really hate these bland, barely actionable advices with no | examples, created for SEO purposes. | | Nobody knows how to measure the "complexity" and "evolvability" | of the API and where exactly is the threshold that justifies | going from REST to GraphQL. | | So here's my opinionated alternative: | | Use REST everywhere, unless you can justify the exception. | | A good exception is when you don't know the audience and use | cases for your API. E.g. if you have hundreds of different apps | using the same API and you have no idea _how_ they are going to | use it. You know who does it? Facebook. | | If you're just dogfooding an API for your website, you don't need | GraphQL. If you need one service to talk to another, you don't | need GraphQL. ___________________________________________________________________ (page generated 2023-03-03 23:00 UTC)