Remix.run Logo
gavinray 2 days ago

I'm probably about as qualified to talk about GraphQL as anyone on the internet: I started using it in late 2016, back when Apollo was just an alternate client-side state/store library.

The internet at large seems to have a fundamental misunderstanding about what GraphQL is/is not.

Put simply: GQL is an RPC spec that is essentially implemented as a Dict/Key-Value Map on the server, of the form: "Action(Args) -> ResultType"

In a REST API you might have

  app.GET("/user", getUser)
  app.POST("/user", createUser)
In GraphQL, you have a "resolvers" map, like:

  {
    "getUser": getUser,
    "createUser": createUser,
  }
And instead of sending a GET /user request, you send a GET /query with "getUser" as your server action.

The arguments and output shape of your API routes are typed, like in OpenAPI/OData/gRPC.

That's all GraphQL is.

andrewingram 2 days ago | parent | next [-]

As someone who’s used GraphQL since mid-2015, if you haven’t used GraphQL with Relay you probably haven’t experienced GraphQL in a way that truly exploits its strengths.

I say probably because in the last ~year Apollo shipped functionality (fragment masking) that brings it closer.

I stand by my oft-repeated statement that I don’t use Relay because I need a React GraphQL client, I use GraphQL because I really want to use Relay.

The irony is that I have a lot of grievances about Relay, it’s just that even with 10 years of alternatives, I still keep coming back to it.

maxcan a day ago | parent [-]

Can you elaborate? I've used URQL and Apollo with graphql code gen for type safety and am a big fan.

What about relay is so compelling for you? I'm not disagreeing, just genuinely curious since I've never really used it.

andrewingram a day ago | parent | next [-]

For me it’s really about the component-level experience.

* Relatively fine-grained re-rendering out of the box because you don’t pass the entire query response down the tree. useFragment is akin to a redux selector

* Plays nicely with suspense and the defer fragment, deferring a component subtree is very intuitive

* mutation updaters defined inline rather than in centralised config. This ended up being more important than expected, but having lived the reality of global cache config with our existing urql setup at my current job, I’m convinced the Relay approach is better.

* Useful helpers for pagination, refetchable fragments, etc

* No massive up-front representation of the entire schema needed to make the cache work properly. Each query/fragment has its own codegenned file that contains all the information needed to write to the cache efficiently. But because they’re distributed across the codebase, it plays well with bundle size for individual screens.

* Guardrails against reuse of fragments thanks to the eslint plugin. Fragments are written to define the data contract for individual components or functions, so there’s no need to share them around. Our existing urql codebase has a lot of “god fragments” which are very incredibly painful to work with.

Recent versions of Apollo have some of these things, but only Relay has the full suite. It’s really about trying to get the exact data a component needs with as little performance overhead as possible. It’s not perfect — it has some quite esoteric advanced parts and the documentation still sucks, but I haven’t yet found anything better.

Did my only ever podcast appearance about it a few years ago. Haven’t watched it myself because yikes, but people say it was pretty good https://youtu.be/aX60SmygzhY?si=J8rQF6Pe5RGdX1r8

tcoff91 a day ago | parent | prev [-]

Try gql tada it’s much better than graphQL codegen

maxcan 19 hours ago | parent [-]

I did. I really wanted to like it. I think it broke due to something I was doing with fragments or splitting up code in my monorepo. I may give it a shot again, from first principles it is a better approach.

jayd16 2 days ago | parent | prev | next [-]

This seems a bit reductive as it skims over the whole query resolution part entirely.

verdverm 2 days ago | parent | next [-]

Which is where the real complexity comes in

thom 2 days ago | parent | prev [-]

This, for me, is a perfect description of the entirety of GraphQL tbh.

8n4vidtmkvmk 2 days ago | parent | prev | next [-]

I think you're oversimplifying it. You've left on the part where the client can specify which fields they want.

verdverm 2 days ago | parent [-]

That's something you should only really do in development, and then cement for production. Having open queries where an attacker can find interesting resolver interactions in production is asking for trouble

Aurornis a day ago | parent | next [-]

> That's something you should only really do in development, and then cement for production

My experience with GraphQL in a nutshell: A lot of effort and complexity to support open ended queries which we then immediately disallow and replace with a fixed set of queries that could have been written as their own endpoints.

twodave a day ago | parent [-]

This is not the intended workflow. It is meant to be dynamic in nature.

fgkramer 2 days ago | parent | prev | next [-]

But has this been thoroughly documented and are there solid libraries to achieve this?

My understanding is that this is not part of the spec and that the only way to achieve this is to sign/hash documents on clients and server to check for correctness

verdverm 2 days ago | parent | next [-]

Well, it seems that the Apollo way of doing it now, via their paid GraphOS, is backwards of what I learned 8 years ago (there is always more than one way to do things in CS).

At build time, the server generates a random string resolver names that map onto queries, 1-1, fixed, because we know exactly what we need when we are shipping to production.

Clients can only call those random strings with some parameters, the graph is now locked down and the production server only responds to the random string resolver names

Flexibility in dev, restricted in prod

girvo a day ago | parent | prev [-]

I mean yeah, in that Persisted Queries are absolutely documented and expected in production on the Relay side, and you’re a hop skip and jump away from disallowing arbitrary queries at that point if you want to

Though you still don’t need to and shouldn’t. Better to use the well defined tools to gate max depth/complexity.

verdverm 15 hours ago | parent [-]

All these extra requirements are why GraphQL never really captured enough mindshare to be a commonly selected tool

girvo 11 hours ago | parent [-]

> GraphQL never really captured enough mindshare to be a commonly selected tool

It has been, at the scale it matters and should be used at. Most companies don't operate at that scale though.

hdjrudni 2 days ago | parent | prev [-]

Sure, maybe you compile away the query for production but the server still needs to handle all the permutations.

verdverm 2 days ago | parent [-]

yup, and while they are fixed, it amounts to a more complicated code flow to reason about compared to you're typical REST handler

Seriously though, you can pretty much map GraphQL queries and resolvers onto JSONSchema and functions however you like. Resolvers are conceptually close to calling a function in a REST handler with more overhead

I suspect the companies that see ROI from GraphQL would have found it with many other options, and it was more likely about rolling out a standard way of doing things

ericyd 2 days ago | parent | prev | next [-]

Is this relevant to the posted article? I don't see how the OP misrepresents anything about GQL.

mirekrusin 2 days ago | parent | prev | next [-]

Except you can't have ie. union as argument, which means you can't construct ie. SQL/MongoDB-like where clauses.

rbalicki a day ago | parent [-]

This is a genuinely accurate critique of GraphQL. We're missing some extremely table-stakes things, like generics, discriminated unions in inputs (and in particular, discriminated unions you can discriminate and use later in the query as one of the variants), closed unions, etc.

scotty79 a day ago | parent | prev [-]

GraphQL is best if the entire React page gathers all requirement from subcomponents into one large GraphQL query and the backend converts the query to a single large SQL query that requests all the data directly from database where table and row level security make sure no private data is exposed. Then the backend converts SQL result into GraphQL response and React distributes the received data across subcomponents.

Resolvers should be an exception for the data that can't come directly from the database, not the backbone of the system.