Introduction to GraphQL
Amazee Labs is heavily invested in exploring and adopting new techniques and technologies. For nearly two years we’ve been openly exploring Decoupled Drupal. Among the things that we’ve researched during this time, React and GraphQL certainly stand out as two of the sharpest new tools in our ever growing toolbox. Based on these, we’ve since established a full application architecture and infrastructure.
In the upcoming weeks leading up to DrupalCon Vienna, we want to share our experiences and learnings in a series of blog posts.
While the Drupal community is growing more and more aware of emerging frontend technologies like Ember, Vue, React or Elm, one of the crucial cornerstones of our architecture, GraphQL, has gone largely unnoticed.
We’d like to begin our blog post series by shedding some light on it and how we use it to construct a robust, flexible and extendable data bridge between Drupal and our React frontend.
The web already features a wide range of introductions, blog posts and tutorials covering every little detail about the syntax / language and core concept. In fact, the amazing people of the Apollo team have even built a playground for effortless experimenting. I’d like to begin this blog post series by giving a quick introduction to GraphQL along these lines before further exploring it from the point of view of a Drupal developer or site builder and highlight its potential in the context of Decoupled Drupal.
For this reason, let’s keep the raw introduction to a minimum. If you have absolutely no clue what GraphQL is and the following quick introduction does not satisfy your thirst for knowledge, I’d encourage you to refer to the official documentation and language specification document.
An application layer query language
As the name suggests, we are talking about a query language. Don’t let yourself be fooled though, unlike what you might associate with this term initially, GraphQL is not the new SQL.
GraphQL is an application layer query language, meaning that, after parsing the submitted query string, it runs arbitrary code on your application to resolve the requested data. As such, it is completely agnostic of your underlying storage.
Based on a schema
A GraphQL server is backed by a hierarchical schema describing the possibilities of the server. The schema consists of an arbitrary combination of types. Complex types contain fields that execute functions, potentially with provided arguments, to compute or load (or mutate) data. These functions integrate the schema with the application layer.
Because GraphQL simply runs arbitrary code on the application layer, it is agnostic of the underlying storage layer. Therefore, schemas tend to be product centric and resemble the user-facing application model instead of exposing storage details.
Since GraphQL schemas are based on a type system, they are “introspective” or “self-documenting”. It is possible to retrieve the schema structure and documentation of a GraphQL server through a special introspection query.
Empowering the consumer
Within the boundaries specified by the schema, clients can run arbitrary queries against the server. This changes the traditional client-server relationship by shifting power to the client.
GraphQL helps where your client needs a flexible response format to avoid extra queries and / or massive data transformation with the overhead of keeping them in sync.
Using a GraphQL server makes it very easy for a client side developer to change the response format without any change on the backend.
With GraphQL, you can describe the required data in a more natural way. It can speed up development because, in application structures like top-down rendering in React, the required data is more similar to your component structure.
The anatomy of a query
A single request, also known as document, may contain multiple operations. An operation is either a query (fetch data), a mutation (write and fetch data) or a subscription (continuously fetch data). Operations can be named and may include variable definitions that can be used as field arguments within the operation.
A field is a queryable unit on your schema. Fields can be aliased which comes in handy if you want to fetch the same field twice using different arguments. Within your schema, fields are the building blocks that execute code to load, compute or otherwise retrieve and return data.
Fields can expose arguments. These are passed into the resolver function on the server. Most commonly, arguments are simple scalar values like strings, integers or enums but it is also possible to pass full objects.
Operation names are required if you want to send multiple operations in a single request. For single operations, they are optional. Their main purpose is to ease debugging and server-side logging.
This can be “query”, “mutation”, or “subscription”. The different operation types are structurally very similar. However, while queries and subscriptions are pure read operations, mutations manipulate data on your server. What’s interesting is, that mutations can also provide access to the mutated data which essentially makes them queries with preceding side-effects.
Instead of inlining dynamic field arguments in your query document, you can use variable definitions. Variable definitions allow you to send the same query document with different variables. This is a critical feature e.g. for enabling persisted queries.
A set of fields requested in an operation, or nested within another field. Selection sets are required on any field that returns a fieldable type.
A directive is a flag on a field, fragment, or operation capable of conditionally including or excluding the affected section of the query. Directives can be added to fields, fragment spreads or inline fragments.
Fragments are used to describe type dependent selection sets.
It is possible to define named fragments at the document level. These uniquely named fragment definitions can then be re-used within any of the operations or within other fragments.
The fragment name is used to embed the fragment in an operation or in other fragment definitions.
The type condition specifies which type the fragment applies to.
When referencing a named fragment in your operation or in another fragment, you do so using the spread (...) operator.
Inline fragments are very similar to named fragments. Unlike named fragments, however, they are written as part of the query and can also be used with only a directive instead of a type condition.
In the follow-up blog post we are going to dive into Drupal and explore how the GraphQL module enables you to quickly and conveniently turn Drupal into a versatile data hub.