Having worked extensively with OpenAPI, GraphQL, plain JSON/HTTP, and gRPC/Buf Connect services, most of this rings true for me.
One thing the author doesn't mention is that you can limit the set of queries clients can call in a GraphQL service, by hash or signature. This mitigates a lot of the gnarly performance and security issues because the attack surface goes from "arbitrary queries" to "queries you've already 'vetted'", where you can look at things like complexity, ACL behavior, etc ahead of time. Since clients (in my experience) aren't usually modifying GQL queries at runtime, this is a pretty good tradeoff.
All that said, I find Buf Connect to be the best set of tradeoffs for most projects: strongly typed servers and clients, strong ecosystem support around protobuf (e.g. to generate an OpenAPI spec), a standard HTTP/JSON interface for easy curl et al compatibility, etc.
OpenAPI as the source of truth is annoying because it's too flexible, and it's rarely possible to generate type-safe servers + clients from an OpenAPI spec without running into one edge case or another.
As this is being downvoted can someone explain why this wouldn't be true? One of the core tenets of graphql was not having to involve the backend team, wasn't it?
In places I've seen this used, front-end developers can run any query in development environments. In production (and sometimes staging) environments, queries must be allowlisted.
This gives the front-end developers lots of flexibility when initially developing new screens and components. Once the UI is ready to ship, the backend team checks to make sure that performance is acceptable (optimizing if necessary), allowlists the new query/queries, and ships to production.
GraphQL is meant to be used along side a very smart client-side cacheing library like Apollo
The best practice for GQL is to make frequent, small, queries (Apollo handles batching them) throughout your application. Apollo won't do any extra work to fetch new fields if they're already in the cache
Not to be that person because I understand there's always edge cases, but in general with GQL if your queries are highly complex or too nested "you're doing it wrong™"
"Make frequent small queries and let a smart cache do the right thing every time" sounds like "make a perpetual motion machine". It just sounds like a fundamentally difficult problem with unavoidable tradeoffs. I admit I don't know the details of Apollo, but I find it hard to believe that a caching layer magically solves everything without introducing very gnarly bugs. A cache layer makes concurrent editing harder, for one.
The Apollo cache doesn't magically solve everything, certainly not concurrency. It's a state store like redux/pinia/mobx, with automatic normalization plus other goodies like being able to query/mutate the store locally with gql if you want. Doing N+1 cache-only queries is also no big deal, though I don't do so myself. They probably should have called it a state store and not a cache, but whatevs...
+1 for Buf Connect. Great CLI, simple codegen configuration, basically no added runtime complexity — it’s just the serialization layer at that point. It’s also great to be able to use it for both the API layer using JSON while also allowing full gRPC inter-op between backend services, with the same library and workflow.
using OPENAPI, rpc, GQL types in client, etc to share typing (schema) information between client/server
resolver/dataloader in GQL, eager join in ORM is to handler internal data composition
presenter layer should not care about data composition, so that writing Query at presenter is an anti-pattern.
presenter should fetch schema info & data passively, like what https://github.com/hey-api/openapi-ts did, the job of implementation belongs to backend.
In fact what rest/rpc really need is the resolver and dataloader, to help backend easily extend or composing data together, and immediately transferring the schema & data to clients.
pydantic-resolve is the python binding for this idea.
Buf Connect or any RPC design really is great. I'm sick of REST too. No more endless discussions about how to make this endpoint the most RESTful or how to cram a feature into REST that doesn't fit. "Oh you need an endpoint to hibernate the server? Just POST a new Hibernate object to the /api/v2/hibernations service."
No. With RPC we can just make a HibernateServer call and be done with it.
RTF tried to make it clear in his famous dissertation that REST was about resource-oriented hypermedia, and that apps not designed around hypermedia should use different architectures -- he even names some of them. Unfortunately he inspired many followers to interpret it to mean that hypermedia is the One And Only True Way to design apps for the web.
One thing the author doesn't mention is that you can limit the set of queries clients can call in a GraphQL service, by hash or signature. This mitigates a lot of the gnarly performance and security issues because the attack surface goes from "arbitrary queries" to "queries you've already 'vetted'", where you can look at things like complexity, ACL behavior, etc ahead of time. Since clients (in my experience) aren't usually modifying GQL queries at runtime, this is a pretty good tradeoff.
All that said, I find Buf Connect to be the best set of tradeoffs for most projects: strongly typed servers and clients, strong ecosystem support around protobuf (e.g. to generate an OpenAPI spec), a standard HTTP/JSON interface for easy curl et al compatibility, etc.
OpenAPI as the source of truth is annoying because it's too flexible, and it's rarely possible to generate type-safe servers + clients from an OpenAPI spec without running into one edge case or another.