I wrote What I Learned from Reading the GraphQL Spec - Part 1, pointing out several noteworthy facts that stood out to me while reading the GraphQL spec. As there is still so much more going on in the spec, here is the second part.

The polymorphic input union type is just around the corner

Adding support for the polymorphic input union type in the GraphQL spec will allow us to provide values of different types to the same field. For instance, given a "oneof" input object EmployeeByInput:

type Query {
  employee(by: EmployeeByInput!): Employee
}

input EmployeeByInput @oneOf {
  id: ID
  email: String
  departmentRank: DepartmentRank
}

input DepartmentRank {
  department: Department
  position: Position
}

enum Department {
  # ...
}

enum Position {
  # ...
}

...we can fetch an entity of type Employee by providing either the id, the email or an input of type DepartmentRank to the same Query.employee field:

{
  programmer: employee(by: { id: 1 } ) {
    name
  }
  consultant: employee(by: { email: "gaston@suovenir.com" } ) {
    name
  }
  manager: employee(by: { departmentRank: { department: SALES, position: MANAGER } }) {
    name
  }
}

The @oneOf directive is currently missing from the GraphQL spec, but as there is progress on PR #825 we can expect it to be added to the spec sooner than later. (More info in article Coming Soon To GraphQL: The oneof Input Object.)

Directive order matters

The directives section on the spec defines:

The order in which directives appear may be significant, including repeatable directives.

Repeatable directives make it evident that altering the order in which directives are applied will produce a different value. For instance, applying directive @append twice on the same field will produce a different result if modifying their order:

directive @append(text: String!) repeatable on FIELD_DEFINITION

type Post {
  content: String
    @append(text: " - Copyright 2022")
    @append(text: " - leoloso.com")
}

White spaces, line breaks and commas add no semantic value

This fact may initially appear evident, but it happens only because it is actually specified in the spec: adding white spaces (outside a string or a comment), line breaks and commas in the GraphQL query will not affect how it is resolved.

As the White Space section indicates (and similarly in the contiguous Line Terminators and Insignificant Commas sections):

White space is used to improve legibility of source text and act as separation between tokens, and any amount of white space may appear before or after any token. White space between tokens is not significant to the semantic meaning of a GraphQL Document [...]

That's why these queries are equivalent (with the exception of the location of each token, which the GraphQL server may display to indicate the source of an error):

query{posts(limit:3 offset:2) {id title author {id name}}}

query { posts( limit: 3, offset: 2 ) { id, title, author { id, name } } }

query {
  posts(
    limit: 3
    offset: 2
  ) {
    id
    title
    author {
      id
      name
    }
  }
}

Variables must always be declared

This one may seem like a non-issue, but only because variables are currently rather static in GraphQL. For instance, the query below receives variable $limit to know how many posts to retrieve:

query GetPosts($limit: Int) {
  posts(limit: $limit) {
    id
    title
  }
}

In the expected behavior, we provide the variable value in a JSON object defined in the same document. This value is provided by the user, which is what I call "static":

{
  "limit": 3
}

But variables could also be the result of a computation by the GraphQL server when resolving the query, making it "dynamic", as has been requested in issue #583.

For instance, the value for $limit could be calculated by some previous field, and made available to the target field via an @export directive:

query GetPosts($limit: Int) {
  _limit: options(name: "limit") @export(as: "limit")
  posts(limit: $limit) {
    id
    title
  }
}

In the query above, both the computation of $limit and its use happen within the query itself. If there's no expectation to provide this value externally, via the JSON document, then the definition of the variable in the operation could be considered irrelevant.

The definition also establishes the type of the variable, which is indeed useful, but mostly when the value must be provided by someone else; the developer of the query will know exactly the type of the fields, so may not need to declare its type. In addition, from the schema metadata, the GraphQL server can already validate if there's a mismatch between the types of the value-exporting field and the input, and return an error whenever that happens.

Then, since a dynamic variable does not really need a definition, we may want to obviate it (which may also enable removing the operation and its name from the document, as in the case below):

{
  _limit: options(name: "limit") @export(as: "limit")
  posts(limit: $limit) {
    id
    title
  }
}

But this is not possible in GraphQL. Variables must always be defined in the operation, whether they are static (as is the expected behavior) or dynamic (as may be offered by some GraphQL server as a custom feature).

There is no Map (or Tuple) type!

In JavaScript, we can define an object:

const someObject = {
  x: 0.5,
  y: 0.2,
  z: 0.99
}

In PHP, we can define an associative array:

$someObject = [
  'x' => 0.5,
  'y' => 0.2,
  'z' => 0.99
];

And the same case applies to innumerable languages.

But in GraphQL, we cannot declare a field to be of type Map, where each entry has a key and a value. As a consequence, even though the GraphQL server is coded on JavaScript or PHP (or anything else), or fetches its data from some application that is coded with JavaScript or PHP, the GraphQL API cannot naturally represent the associative data from the source, and must resort to some hack in order to do so.

Adding a Map or Tuple type has been requested:

I wonder if a Map or Tuple will ever be added to the spec. I'd really love to be able to use any of them!

We can add "extensions" to output custom data

The GraphQL spec describes in detail how the response must be formatted, which involves defining what top-level entries must be used in the returned map: the queried data is added under entry data, and the errors under entry errors.

But sometimes we need to output some additional information, such as logs, warnings or suggestions. These entries are not covered by the spec, and we are banned from adding them under their own top-level entry. Instead, the GraphQL spec provides a special location which we can fill as we desire, to pass whatever custom data we want: the top-level extensions entry. As explained in the Response Format section:

The response map may also contain an entry with key extensions. This entry, if set, must have a map as its value. This entry is reserved for implementors to extend the protocol however they see fit, and hence there are no additional restrictions on its contents.

We can then extend the response of the GraphQL API to offer a suggestion to our users, like this:

{
  "extensions": {
    "suggestions": [
      {
        "message": "Want to be on the bleeding edge? Check out our new endpoint /graphql/nightly/"
      }
    ]
  },
  "data": {
  }
}

There are no generic error codes, only validations to perform

There are languages and frameworks which keep track of all the possible errors that may happen, and have assigned a specific error code to each. Then, when an error arises, its corresponding error code is returned to help the developer find the source of the problem, and provide a link explaining why the issue happens. Examples include React, Rust and TypeScript.

GraphQL finds itself in a middle ground: the specification contains an extensive Validation section defining and explaining all the validations that the GraphQL server must perform when resolving the query, however, these do not have some unique and specific error code assigned to them. As such, whenever an error is returned in the GraphQL response, we will normally be offered a description of the error but not a representative error code or URL explaining why it happens and how to fix it.

This situation has been brought into attention in issues #698 and #708. I wonder if support for generic error codes will ever make it into the spec.

Emojis are not supported 😢, but will very soon 🙏

We can't currently use emojis inside the GraphQL document, as in this example:

query {
  # 😱 TODO: add `date` field
  posts {
    id
    title
  }
}

Fortunately, there is PR #849 tackling it and, since it's in "RFC 3: Accepted", it's only a matter of time until it is merged into the spec 👏.

It is GraphQL, but it could've been TreeQL

To finish I'll share an amusing fact, which I've learn about by reading the notes from the monthly sessions by the working group: because the shape of the response is a tree (which is a particular type of a graph), the creators of GraphQL considered calling it TreeQL!

Lee [Byron]: it was added very deliberately, anecdotally we return trees not graphs and it was argued early on that we should call it TreeQL.

Introspection is a graph, but the response is always a tree.

I don't know why they changed their mind, but I must say that "GraphQL" sounds way better.

Conclusion

As I mentioned in part 1 of this series, I strongly recommend that anyone building a GraphQL API reads the GraphQL spec: it is well-written and concise, and it can give us plenty of insights to understand how the GraphQL server works and, as such, make the most of our APIs. For my part, I'll keep reading the spec continuously and, as it keeps being updated, I may find new noteworthy details to share in this blog.