The GraphQL spec is not a done deal. Instead, it is a work in progress, fueled by a vibrant community which adds new guidelines in an ongoing manner. Even though the latest release dates to June 2018, the working draft already contains all the changes slated to become part of the upcoming new release.

One of the most exciting aspects concerning GraphQL takes place in the GraphQL spec's repo. Since this is a specification, what is debated in the issues board are concepts and ideas, rather than implementations or bugs (which will happen on the repo of each GraphQL server, particularly for the reference server implementation, graphql-js).

As a matter of fact, I love browsing these issues, to appreciate what new features may eventually make it to the spec. Indeed, GraphQL server vendors may attempt to already provide support for compelling features (for instance under some experimentation flag), well in advance of their approval for the spec.

Many of the proposals (if not most of them) may end up being rejected, since their implementation may not be technically sound, or too difficult to achieve. But even then, their concepts may be quite simple and powerful, and they are worth exploring just for the sake of it.

For this article, I have selected 5 of the requested issues that I find most compelling, and describe how they would work.

Let's start!

Flat chain syntax

The flat chain syntax (#174) would enable us to create shortcuts in our queries, so that we can simplify the client-side code that will read the retrieved data.

That is the case when fetching a single field from a connection, such as:

{
  clients(limit: 5) {
    id
    programs {
      shortName
    }
  }
}

The response for this query will have programs entries, each of them containing a list of objects with only one element:

{
  "data": {
    "clients": [
      {
        "id": 2,
        "programs": [
          {
            "shortName": "ABC"
          },
          {
            "shortName": "DEF"
          }
        ]
      },
      {
        "id": 3,
        "programs": [
          {
            "shortName": "ABC"
          },
          {
            "shortName":  "XYZ"
          }
        ]
      }
    ]
  }
}

We can fetch the list of shortName values in JavaScript like this:

data.clients.forEach(client => {
  const shortNames = client.programs.map(program => program.shortName)
  console.log(shortNames);
});

However, since each object in programs contains only one element, the key name "shortName" could be perfectly obviated, and we could directly obtain the value, like this:

{
  "data": {
    "clients": [
      {
        "id": 2,
        "programs": ["ABC", "DEF"]
      },
      {
        "id": 3,
        "programs": ["ABC", "XYZ"]
      }
    ]
  }
}

Now, iterating the list of shortName values in JavaScript becomes simpler:

data.clients.forEach(client => {
  const shortNames = client.programs;
  console.log(shortNames);
});

Or even:

const shortNames = data.clients.map(client => client.programs)
console.log(shortNames);

With the flat chain syntax, querying the nested field shortName would be done using the dot notation:

{
  clients(limit: 5) {
    id
    programs: programs.shortName
  }
}

The shortcuts could also span multiple levels:

{
  clients(limit: 5) {
    id
    programAuthors: programs.author.name
  }
}

And the multiple levels would still be flattened out to a single array:

{
  "data": {
    "clients": [
      {
        "id": 2,
        "programAuthors": ["John", "Peter", "Abdul"]
      },
      {
        "id": 3,
        "programAuthors": ["Carmen", "Rachel"]
      }
    ]
  }
}

I particularly like this proposal not just because it can simplify the client-side code, but because it allows to simplify the GraphQL schema.

I'll reference the WordPress data model to provide an example. In WordPress, a post has an author, and it can have tags. To model it, type Post has field author retrieving a User, and field tags retrieving a list of Tag:

type User {
  id: ID!
  name: String!
}

type Tag {
  id: ID!
  name: String!
}

type Post {
  id: ID!
  title: String
  tags: [Tag]
  author: User!
}

When showing the tags for a post, the name alone will normally suffice. Hence, it is common practice to have Post retrieve the tag names directly, via field tagNames:

type Post {
  tagNames: [String]
}

But I feel that this makes the schema less elegant, because with a similar logic, the Post could also have a field authorName:

type Post {
  authorName: String!
}

Similarly, we could add such relationship shortcuts for many other fields (Post.catNames, User.postTitles, etc). If we do so, the schema becomes cluttered. If we do not, the schema becomes inconsistent.

By having the flat chain syntax, we can avoid adding field Post.tagNames to the GraphQL schema altogether, yet allow the client to query this same "field":

query {
  posts {
    id
    tagNames: tags.name
  }
}

Parameterized fragments

With parameterized fragments (#204), a fragment would be able to declare the variables that it consumes, instead of depending on the variables defined in the operation (as is currently done).

This way, we would be able to code a fragment that works in isolation, independent of its context (i.e. the query it is embedded in).

For instance, a parameterized fragment can declare its variables like this:

fragment avatarFields($thumbSize: Int = 40) on Image {
  thumbURL(size: $thumbSize)
  uploadedAt
}

Then, different queries can supply different inputs into the fragment, as in this case:

query {
  person(id: 1) {
    avatar {
      ... avatarFields(thumbSize: 80)
    }
  }
}

Or in this other case:

query findPerson($personId: Int!, $avatarSize: Int!) {
  person(id: $personId) {
    avatar {
      ... avatarFields(thumbSize: $avatarSize)
    }
  }
}

This proposal is valuable not only because it makes the fragments more understandable, but also because it enables us to use the same mindset of modern JavaScript frameworks for managing code as components, as done by Vue or React.

Union types can implement interfaces

Union types can implement interfaces (#518) addresses a deficiency: an union type whose member types all implement a certain interface could also be considered to implement that interface, but it currently does not.

In the following schema, type MyConnection fails at implementing interface Connection, because type MyNode is not considered to implement interface Node, even though the member types NodeA and NodeB do implement it:

type Query {
  test: MyConnection
}

interface Node {
  id: ID
}

interface Connection {
  nodes: [Node]
}

type NodeA implements Node {
  id: ID
}
type NodeB implements Node {
  id: ID
}
union MyNode =
  | NodeA
  | NodeB

type MyConnection implements Connection {
  nodes: [MyNode]
}

An upgrade to the proposal suggests that union types could explicitly implement the interface:

union MyNode implements Node =
  | NodeA
  | NodeB

As I see it, the value of this proposal is that it would clarify the meaning of the interface designed by the application (as coded in its GraphQL schema) for the user of the API.

For instance, in WordPress there is the concept of a "custom post", which is satisfied by entities "post", "page", and others. Custom posts have common properties, including the title, content and publishing date.

In a GraphQL API in WordPress, there will be types for Post and Page, and since they share the custom post properties, it makes sense to have them implement an interface IsCustomPost which defines the common fields:

interface IsCustomPost {
  title: String
  content: String
  date(format: String): Date!
}

type Post implements IsCustomPost {
  title: String
  content: String
  date(format: String): Date!
}

type Page implements IsCustomPost {
  title: String
  content: String
  date(format: String): Date!
}

The root field customPosts can fetch the list of all the custom posts, that is the list of all Post and Page objects. This is mapped via the union type CustomPostUnion:

union CustomPostUnion = Post | Page

type QueryRoot {
  customPosts: [CustomPostUnion]!
}

This approach already works well, it doesn't need fixing from a technical perspective.

However, it goes against the way I think about the concept of custom posts. In my mind, these are entities whose type implements the IsCustomPost interface. So I'd rather express it like this:

type QueryRoot {
  customPosts: [IsCustomPost]!
}

This proposal could then bridge the gap, by defining that a CustomPostUnion is itself an IsCustomPost, and the intent of what the customPosts field provides becomes clearer.

The ability to represent empty objects

Adding the ability to represent empty objects (#568) would enable us to implement algebraic types, where a type can be returned to express an intent, status or condition. In this case, the type may not need to convey extra information, hence it should be allowed to be empty (i.e. without any field).

For instance, types UserWithEmailAlreadyExists and WeakPassword do not need to provide any information, but returning them is already useful to indicate the status of the operation:

type Mutation {
  createUser(email: String!, password: String!): CreateUserPayload!
}

type UserWithEmailAlreadyExists
type WeakPassword
type AuthPayload {
  user: User!
  token: AccessToken!
}
union CreateUserPayload = UserWithEmailAlreadyExists | WeakPassword | AuthPayload

Support for Tuples

Adding support for tuples (#534) would make it easier to input pairs (or triplets, or relationships of any cardinal) of items that are commonly provided together.

For instance, type Content has a field cards that retrieves a tuple, with a name in the first position, and an object of type CardPayload in the second position:

type CardPayload {
  foo: String!
  bar: Int!
}

type Content {
  cards: [String!, CardPayload!]
}

To retrieve the data, we also pass a tuple, specifying what fields to fetch from the CardPayload object in the second position:

query {
  content {
    cards [name, { foo }]
  }
}

Tuples can simplify a query. For instance, to specify how to sort results, we may currently have an input SortInput:

enum Order {
  ASC
  DESC
}

input SortInput {
  orderBy: String
  order: Order
}

type QueryRoot {
  posts(sort: SortInput): [Post]!
}

With a tuple, the field definition could already define the input items for sorting the results:

type QueryRoot {
  posts(sort: [String, Order]): [Post]!
}

And to query, we pass the input values via a tuple, making it less verbose than passing an input object:

query {
  posts(sort: ["title", DESC]) {
    id
    title
  }
}

Conclusion

The GraphQL spec seeks stability, so we won't find crazy proposals being approved. All of them go through a rigorous process comprising several stages, and will quite likely take months, or even years, before a proposal is finally accepted and makes it to the spec.

Indeed, among the 5 proposals featured in this article, one of them is over 5 years old, another one will celebrate its 5th anniversary in just a few months, and they are both still in the first stage of the process (Strawman (RFC 0)).

Nevertheless, even though we don't know if or when they will be approved, exploring the proposals requested for the GraphQL spec is fun, and I recommend everyone to do it at least once.

Even more, many of the proposals are compelling, but they need a champion to guide them through the process. Sometimes, finding a champion is the difference from a potential cool new feature for the spec, to seeing it become a reality.

In this article I presented 5 of my preferred new feature proposals. Which ones are yours?