Skip to content

The GraphQL Golden Path Initiative #1887

@benjie

Description

@benjie

Integrating GraphQL into your application is fraught with issues: security, performance, complexity, maintenance, and more... but it doesn't need to be this way! All of the pitfalls that people face have been solved (often long-solved, since before GraphQL was released to the public!), but education around them is both poor, and overwhelming. And I don't think education is actually the answer, here.

GraphQL appears simple

At first blush, GraphQL looks incredibly straightforward - type the keys from the JSON you want into GraphiQL, and get the JSON back with populated values! Hooray - no need to read the docs, I get it!

Users see GraphQL's apparent simplicity and run with it, often painting themselves into corners that don't bite them until months down the line, and are then challenging to extricate themselves from. This massive debt going down the wrong path often leads them to the conclusion that GraphQL sucks and results in Hacker News or Reddit threads where a lot of people share their frustrations.

This is our fault, not theirs.

Successful long-term GraphQL deployments use a lot of techniques

GraphQL is not as simple as it first seems. Successful adopters of GraphQL typically make use of all or most of the following practices:

Client and server:

  • Type generation
  • Code generation
  • Operation allowlist (trusted documents)
  • Telemetry, observability, tracing

Client:

  • Normalized cache
  • Query composition
  • Fragment co-location
  • Fragment masking
  • Client-side error handling/boundaries
  • Solid pagination practices (e.g. page two should use a different query, but spread the same fragment)
  • Solid mutation practices (e.g. updating the normalized store correctly)
  • Solid realtime practices (e.g. ensuring re-synchronization on connection loss)
  • Only fetch what you render immediately, data to be rendered later should be fetched later

Server:

  • N+1 solution (e.g. DataLoader, batch resolvers, or Grafast)
  • Robust security practices
    • Limits on tokens, depth, breadth, complexity, execution time, error count, pagination, and more
    • Timeouts for parsing, validation, execution, etc
    • Error masking
    • Rate limits
    • Circuit breakers
  • Careful schema design practices
    • Client-focused schema
    • Action based mutations (e.g. "completeCheckout" or "inviteUserToOrganization") over CRUD
    • Self-descriptive field names, and associated documentation
    • Using SDL as a shared language between client and server
    • Future proofing: mutation payloads, etc
    • Tracking of field usage, including origin of request (e.g. iOS app v1.3.7)
    • Deprecation periods and instructions, leading to safe versionless field removal
    • Error unions
    • Avoiding complex filters that are hard to optimize
    • Knowing when NOT to use GraphQL, e.g. handling of large binary objects, some forms of authentication, etc
  • HTTP caching
  • Object caching

Let's get opinionated

That's a lot of stuff off the top of my head, and I'm pretty sure I've missed major pieces!

I'd argue that users not adopting all of these, and then having a bad time, shouldn't be blamed on users not RTFM-ing. Instead, I think it's the fault of our libraries and frameworks being insufficiently opinionated, and not giving a solid, reliable, and future proof out-of-the-box experience - a golden path - that means that most people get off to the right start without having to spend weeks educating themselves.

The golden path shouldn't be a mandate, experts should still be able to stray from the path as they see fit, but it should be the default experience.

First time user experience

In my opinion, first time users shouldn't need to worry about:

  • the N+1 problem - just give them batch resolvers out of the box (same as regular resolvers, except the first argument is a list), and advise that they only visit traditional resolvers once they're confident they understand the problem space
  • malicious queries - support the trusted documents pattern out of the box on both client and server
  • picking values for various limits - give them broadly applicable defaults that handle most cases, and then ensure that error logging helps guide them to how to resolve
  • configuring their client - just point the tooling at the GraphQL endpoint and have it infer where to go for subscriptions (websockets or SSE), if there's an identity field that should be used, if server-side null propagation should be disabled, and even things like where to send trusted document, where to report their telemetry, and so on.

Specification work

The GraphQL specification already has almost everything it needs to support our libraries implementing this. The only missing piece of the puzzle right now is the client-autoconfiguration (which can be achieved through the "service capabilities" proposal I'm working on), and arguably the onError: NULL work.

The community needs to pull together to make this work!

If we work together on detailing this golden path - defaults, practices and behaviors we can all agree on as a community and can integrate into our software - then users will hopefully be led into the "pit of success". Adopting such practices might mean that GraphQL usage involves a smidge more effort up front for them - learning these new tools and patterns - but will make them much more successful over the longer term, and really show how GraphQL can shine!

Who'd like to help build a "GraphQL Golden Path Outline" that describes what we see as the golden path, to help libraries, frameworks and tools to all conform to sensible defaults, whilst still leaving wide space for innovation and differentiation? I think if we do this well, users won't need to be experts or a mega-corporation with a dedicated GraphQL support team to do GraphQL right; they'll get a great experience out-of-the-box, and can be productive and performant from day one without the frustrations down the line.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions