Comprehensive GraphQL Specification Proposal

Note: this isn’t quite a Schema Design post, but I couldn’t find a more suitable category.

I’ve put some more time last weekend to work on a comprehensive GraphQL specification proposal. It is now almost ready to be submitted to the GraphQL working group and aims to address many of GraphQL’s “rough edges” (pun fully intended), including those both Apollo and we have to deal with in Federation.

GraphQL specification process is objectively slow. We haven’t had a new spec for years now. This is definitely due to challenges of having people and them having enough time to actively sponsor proposals with their involvement and discussions (i.e. not money).

Another problem is that many proposals aim to improve a single thing while leveraging only what is otherwise already available. This is very restrictive, does not enable best solutions and drags the timelines even more as multi-year proposal iterations amount to too much time when considering long dependency chains of otherwise focused / pinpoint proposals.

To combat that, my proposal RFC is actually many component RFCs that leverage each others’ benefits.

Unfortunately, I need help, as I cannot dedicate time that would be sufficient to push all these changes through. Of course, I also need other people’s perspectives to keep me in check. It is quite possible that I missed some important detail.

To that extent, please do help.

My proposal is currently still in my fork of the GraphQL WG GitHub repo:

I do plan to initiate a pull request relatively soon, barring any issues. At that time the GraphQL working group (WG) will need more contributors’ time than I can give. Apollo: I’m looking at you too - there are many goodies in there that, I think, you’d find very helpful.

1 Like

Here’s a “catalog” of things in there, to wet your Federation-focused appetites:

RFC: Safe navigation (shortcut) and refactoring operators
Do schema composability requirements make your supergraph schemas very complex and deep, forcing clients to deal with additional verbose structures they otherwise wouldn’t have to? Can I just, please, get ids of all people who authored books without having to drill down through two nesting levels? Are you trying to support/expose a legacy REST API by leveraging a native GraphQL service (incl. supergraph) and desire the ability to transform GraphQL model into that legacy REST one?
This one addresses or, at least, helps with that.

RFC: Namespace support
What do you do to avoid naming clashes in supergraphs? What do you involve in governance? What happens if an interface declared in one subgraph gains a field that was previously introduced in its subtype in another supergraph? Hate all the underscores or the inability to introspect chunks of schema at the supergraph level? This one addresses that.

RFC: Allow directives on directives
Sounds trivial and useless… but it is leveraged by the next one:

RFC: Introspectable directives
Apollo had to introduce alternate means of getting the schema out of a GraphQL API (subgraph) because directives aren’t included in the introspection schema - client’s can’t get them. That requires additional code to support Federation in each subgraph. This RC addresses that.

RFC: Operation Types
This has been a topic of multiple discussions. Should something be a query or mutation? Can mutations be nested in queries - e.g.: book(id: 123) { addReview(...) }? Can you use subscriptions beyond the single http connection? How to unsubscribe those?

RFC: Transactions
You had a single (sub)graph and your GraphQL requests were executed as individual transactions. They either worked fully or didn’t at all. You were in control of how to expose them but wished there was a standard so you don’t have to code all of it. Then you entered Federation and found out that you can’t any longer maintain the assumption “all or nothing” as Apollo doesn’t have a way to manage transactions across subgraphs. Apollo isn’t to blame here - there simply isn’t a standard for this. This RFC addresses that.

RFC: Allow empty types
You’re starting a fresh, new, shiny supergraph! You want to do it cleanly and well. Start with some foundations and have subgraphs add to that. Trouble is that your foundational schema requires the declaration of types that subgraphs will add to… but there is nothing to put inside those types within the foundation itself, so GraphQL fights you: “No, you won’t!”. You see the same appear at other levels of hierarchy. This simple RFC solves that problem.

RFC: Permit interface types in concrete output
Apollo’s forced to make rather clumsy workarounds like Entity Interfaces - @interfaceObject, again because they have no better legal way of doing what we need in Federation. This RFC fixes that.

RFC: Type references
Do you dislike resorting to unchecked string names of types in Federation directives? Would you like to be able to do some client-validation on type names? Coupled with the namespaces RFC, would you like to be able to use short names rather than spell out long/verbose fully-qualified names every time? This RFC is for you.

RFC: Enhanced unions
On its own it sounds almost useless - this RFC allows unions of types other than (output) object types. For example, it allows unions of scalars. It also allows unions of types of different kinds - say a scalar and an object type. Why is it important? Well, 2nd RFC down is why, among other reasons.

RFC: Anonymous types
Tired of having to name every. single. thing? Not only you’re forced to keep solving the “hardest problem in computer science” (naming) but you keep “wasting” (using) names, thus forcing all of them to be longer to make them distinct while making your schema larger and less readable. This problem is augmented in Federation - don’t step on other subgraphs’ toes. Let’s have anonymous (nameless) nested types.

RFC: In-data errors
See Apollo’s own Errors as Data Explained. What’s missing?

  • Try to create a union of a scalar type and an error object
  • How do generic clients or Apollo router know if something is an error there?
  • Do you have to keep creating unions every time, for every field?

This RFC leverages others to make this a much better, smoother experience.

RFC: Input Unions
@oneOf is coming to a theater near you… You may have enjoyed a bootlegged pre-release copy already. But it remains verbose and requires manual input type maintenance every time options are added. It is a solution for input argument selection but it isn’t a true answer for input unions. This adds them.

RFC: Input expressions that enable powerful DSLs
How do you pass the search criteria to your search “fields”? Ad hoc bunch of args? Some SQL-like text in a single string value? Looked at DGraph-like structures but found them unwieldy? Do all your subgraphs use the same approach? Could Apollo router rely a standard approach to expose additional functionality on their end? This makes DGraph-like queries palatable as easy and smooth as what you may have wanted all along, without forcing you to expose anything you don’t want to. Better yet, if it becomes a standard, Federation can start doing things on the input side too.

RFC: Privacy and security directives
How do you communicate the security, sensitivity of data, required handling, constraints on caching and communication, etc? This really needs to be a standard to ensure that all the tools in the pipeline honor what’s specified.

RFC: Regular expressions support
Perhaps the least useful one. I just see it all the time. Other basic/standard type libraries should be created.

EDIT:
RFC: Echoes - reflecting outputs back to inputs
This one’s fun. Not much to do with federation, though Apollo may find uses for it on its side.

1 Like

Howdy :waving_hand:

Thanks for opening this. I’ve been quite involved in the nullability and introspection proposals personally. I’d love to see something like this happening!

As you state, there are a lot of existing users and tradeoffs involved. Evolving the spec is never easy! What I’m unclear of is what is there to gain by bundling multiple proposals together vs doing smaller steps? I have always operated with the assumption that smaller changes were easier to move but looks like you have a different view on this?

Thanks for connecting!

I have always operated with the assumption that smaller changes were easier to move but looks like you have a different view on this?

I am not ready to call it a “view” yet… as it is nudging on “hope”, “experiment” or “attempt”. I tried explaining it in my opening post, but let’s try another way that GraphQL afficionados may understand.

GraphQL Specification RFC latency is huge and the already identified improvements opportunities are huge. “Smaller changes” amount to “underfetching”. Were it not for latency, this RESTful-like interaction would be fine… but that’s not the case.

What may be the solution? Well, replace REST-like process with a GraphQL-like one and create a bundled requests that doesn’t underfetch and pays a single “trip” latency. Of course, this increases the “load” in one, thus requiring more resources for a single request… and those resources are actual humans - us. There’s where my call for help comes in.

Additionally, this is a bit of a transaction, not just about raw “performance”. Including multiple RFCs that work well with each other has other effects too:

  1. Desires exist independently of others. By allowing RFC to immediately build upon other RFCs, we reduce or eliminate the need to introduce subpar solutions based on what is available at the time.
  2. Illustrates and/or proves usefulness of the features defined in referenced RFCs. Some RFCs would otherwise look pointless.
  3. Allows circular dependencies between individual RFCs (not that I have those here, didn’t check yet).
  4. Illustrates greater vision all at once, that may have profound effect on the future.
  5. Spares tool developers from multiple large changes or refactorings, perhaps at the cost of a single large(r) one.
  6. I hope that it motivates the community when they see more than superficial changes.