💯 Generating the schema with independently versioned fields and directives

— 8 minute read

A few weeks ago I had added the ability to independently version fields and directives in GraphQL by PoP, by passing the version constraint as a field/directive argument when executing the query. This works well but has a problem: that specific version of the schema could be accessed only by running the query, but not by introspection. So we couldn't run clients or tools against it.

Today I have fixed this issue. Now, the specific version for a field/directive can be provided to the endpoint through URL params, so it can be incorporated when generating the schema artifact. This way, clients and tooling have complete visibility of all possible versions of the schema, allowing to:

  • have a client coded in TypeScript be generated by pointing to the exact required schema
  • point to a different version of the schema just by modifying the endpoint (eg: useful to wait for feature flags to kick in)
  • visualize the schema in GraphQL Voyager, run queries in GraphiQL

Params permalink

Two new URL params to pass to the endpoint were created:

  • fieldVersionConstraints[]
  • directiveVersionConstraints[]

These params are arrays, so they can be defined multiple times in the URL as to version more than 1 field or directive. Directives are referenced directly by their name (without the @), like this:

?directiveVersionConstraints[makeTitle]=^0.1&directiveVersionConstraints[upperCase]=~0.2

Fields are referenced as a combination of their type and their name, separated with a dot, like this:

?fieldVersionConstraints[Post.title]=0.3.1&fieldVersionConstraints[User.name]=0.2|0.3

The type name can be namespaced or not, it will work in either case, with automatic namespacing enabled or not.

Now, the algorithm follows this order to obtain the versioning of a field:

  1. Versioning specified by field argument
  2. Versioning specified by URL param fieldVersionConstraints[] on the namespaced type
  3. Versioning specified by URL param fieldVersionConstraints[] on the non-namespaced type
  4. Versioning specified by URL param versionConstraint

For directives it is similar, but in only 3 steps since they are not namespaced:

  1. Versioning specified by directive argument
  2. Versioning specified by URL param directiveVersionConstraints[] on the directive
  3. Versioning specified by URL param versionConstraint

If no versioning is found on any of these steps, then the field or directive is not versioned.

Demo permalink

Let's check out some examples. In the GraphiQL clients below, the URL parameters fieldVersionConstraints[] and directiveVersionConstraints[] were added to the endpoint /api/graphql/ (check it out in the Network tab in Firefox/Chrome DevTools).

In this query, field userServiceURLs is queried with version 0.1.0 set by field argument, and with version 0.2.0 by default, as set through URL param fieldVersionConstraints[Root.userServiceURLs]=^0.2:

To double check that the default field has version 0.2.0, we can click on the documentation explorer, browse to Root.userServiceURLs, and read the version added to its description:

Services used in the application: GitHub data for a specific repository (Version: 0.2.0)

Or we can also visualize it with GraphQL Voyager, which displays the schema for the specified exact combination of field and directive versions:

Visualizing the field versioning by params in GraphQL Voyager
Visualizing the field versioning by params in GraphQL Voyager

It works the same way for directives. In this query, directive makeTitle is queried with version 0.2.0 set by directive argument, and with version 0.1.0 by default, as set through URL param directiveVersionConstraints[makeTitle]=^0.1:

Of course they can be combined. In this query we are independently setting the version for fields userServiceURLs and userServiceData, and directive makeTitle:

What's next permalink

I have added tons of new features to GraphQL by PoP lately. It is now finally time to work on providing documentation (so I am not the only one who can get to use it!). For that, the following weeks I will be completing the documentation on the newly-launched GraphQL by PoP site.

GraphQL by PoP is currently available for WordPress, to be installed via Composer. In the upcoming weeks/months, I will attempt to release the WordPress plugin, which will be very easy to install, and will contain several wonderful features:

  • Persisted GraphQL queries
  • Access Control Lists, to control who can access the schema, set-up on a field-by-field basis
  • HTTP caching, configured through Cache Control Lists, to define the Cache-Control max-age on a field-by-field basis (and it will be smart: if a field cannot be cached, then the whole request will not be cached!); and internal caching for expensive operations

Most of the features are ready, and I can already say: they are so awesome! Check out this screenshot:

Preview of an Access Control List in the WordPress plugin (WIP)
Preview of an Access Control List in the WordPress plugin (WIP)

Exciting times are coming!

Don't be shy, contact me (I'm here to help) permalink

If you install GraphQL by PoP and run into any trouble, let me know and I'll help: DM me on Twitter, chat on the GraphQL Slack channel, or email.

Arrivederci! 👋