- Published on
The state of GraphQL with Drupal 10 (part 1)
- Authors
- Name
- Christophe Jossart
- @colorfield
In this fist part, we will compare the key differences between the Drupal GraphQL module versions 3 and 4, where to start and various ways to write a schema with version 4. Next articles will focus on specific topics like translation, mutation, persisted queries and decoupling with Gatsby or Next.
GraphQL 3 and 4
TLDR; to start a new project, I won't hesitate for a second and go for GraphQL 4. There is no upgrade path between these two major versions, so using 3 and planning to go for 4 will require a significant migration work.
If you are looking after a comparison of GraphQL with other options, Dries explains it in this post: Headless CMS: REST vs JSON:API vs GraphQL
Here is a summary of the differences between versions 3 and 4.
For both, we will illustrate with an example of a basic Article node type query.
GraphQL 3
- Is providing the schema out of the box, exposing basically everything from Drupal that is content related
- This does not cover Configuration Entities out of the box
- The schema contains many Drupalisms, so it might be harder to grasp by frontenders / API consumers that are not used to Drupal
- The schema can be extended via a Plugin system, mostly
Fields
andTypes
- There is a solution for progressive decoupling with Twig + also a few contributed modules for use cases like preview, formatters, ...
- It relies on a version of webonyx/graphql-php that is not compatible with PHP 8.1. It's working fine with PHP 8.0 but as this PHP version will not receive security support after 26 November 2023 and Drupal 10 requires PHP 8.1, this issue needs to be tackled to be Drupal 10 ready. There is a first attempt.
Here is an example query to get an Article by ID.
{
nodeById(id: "1") {
__typename
entityLabel
entityId
entityBundle
entityUrl {
path
routed
}
... on NodeArticle {
title
nid
sticky
body {
value
format
processed
}
fieldTags {
targetId
entity {
entityId
tid
entityBundle
weight
... on TaxonomyTermTags {
description {
value
format
processed
}
}
}
}
}
}
}
To get multiple nodes, this can be done with a nodeQuery
that will list all node types by default. Here is how to filter by Article node type.
{
nodeQuery(filter: { conditions: { field: "type", value: "article", operator: EQUAL } }) {
count
entities {
entityId
}
}
}
As we can see, it's pretty complete by default and we can get data from the Entity Type (Node) or its Bundle (Article) but this duplication with different field names can be confusing for the API consumer. Because the naming and structure is Drupal specific this can lead to questions for non Drupal developers:
- why are entityUrl and body not a scalar (String)
- what is a bundle
- are entityLabel and NodeArticle.title producing the same values
- same question for entityId and NodeArticle.nid
- why do we have body for Article and description for Tag
- can we query with articleById, or articlesQuery
- what is a TaxonomyTerm
- ...
GraphQL 4
- Recommended by the project maintainers
- Developers have to write the schema, the module is mostly a framework to extend and is providing a solid foundation of built-in data producers
- There is full control over the schema structure
- The Plugin system is mostly about
DataProducer
andSchema
/Resolvers
Various ways to get a Schema with GraphQL 4
Custom, Compose, Core Schema like v3 or Directives, let's see what we get!
Go custom
If there are only a few parts of Drupal to expose or if the expected query / output is tightly bound to the domain, this is a good way to go, it's very flexible.
To get started, all that we need is a Schema plugin then a declaration of the schema in a .graphqls
file. A very minimal implementation is provided in this repository as an example.
Then create a server and head to /admin/config/graphql/servers/manage/minimal/explorer to execute the query.
To get an Article, the query is stripped to the bare minimum, is backend agnostic with clearer field names, no need to know what a node, bundle, nid, entityLabel, ... is.
{
article(id: 1) {
id
title
author
}
}
If we use the schema provided by the GraphQL Example module here is how a list of articles query looks. It will use the default limit (10) and offset (0). Neat.
{
articles {
total
items {
id
}
}
}
Now let's take a look at what the community offers. At the time of writing, all solutions below are usable but as they are quite recent, might still contain some bugs. So keep watching their respective issue queue.
GraphQL Compose
This project is sponsored by Octahedroid and inspired by Open Social GraphQL architecture. There is a possible plan to integrate features from v3 contributed modules (metatag, config, ...).
It currently requires this core_version_requirement patch for Drupal 10.
To get an overview of what is covered by the schema, run this query.
{
composeInfo: graphQLComposeInformation {
name
description
fragments
entityTypes {
id
types {
id
type
}
}
}
}
Here is how to run the Article by id and Articles queries.
The multiple Articles query provides nodes and edges with cursor pagination (first, last, before, after, ...).
fragment NodeArticle on NodeArticle {
id
title
langcode {
id
name
}
image {
__typename
url
width
height
styles {
style
url
}
}
}
{
nodeArticle(id: 1) {
... on NodeArticle {
...NodeArticle
}
}
nodeArticles(first: 5) {
nodes {
__typename
... on NodeArticle {
...NodeArticle
}
}
edges {
__typename
cursor
node {
__typename
... on NodeArticle {
...NodeArticle
}
}
}
}
}
GraphQL Core Schema
This project is sponsored by Liip. Its goal is to expose the schema in a way that is close to what v3 proposes, in a more predicatble way and with the possibility to configure what is exposed (a bit like what JSON:API Extras is doing for JSON:API in core).
We can configure schema extensions (Node, Taxonomy, Entity Display, Entity Query, ...), exposed entity types and fields.
It provides Paragraphs and Views support and some submodules to get e.g. Metatag covered.
To compare with the v3 query, here is a similar Articles query, with Tags.
query Articles {
entityQuery(entityType: NODE, filter: { conditions: [{ field: "type", value: "article" }] }) {
items {
id
label
... on NodeArticle {
body
fieldTags {
label
}
}
}
}
}
For more information and comparison with GraphQL Compose, the maintainers are providing excellent documentation on the project page.
GraphQL Directives
This project is sponsored by Amazee Labs. It is a declarative way to build resolvers, so most of the PHP code written in DataProducers
, Resolvers
or Schema
can be delegated to Directives. This is following a similar philosophy that drives Drupal Commerce: do not expose on the UI what a developer can do / won't be touched often and could produce complexity or unwanted changes from less technical users.
What I like about it is the fine grained way to delegate or write your own resolver, so it provides automation and flexibility over what and how we want to expose.
So a schema that looks like this won't need any custom resolver.
type Article @entity(type: "node", bundle: "article") {
title: String! @resolveProperty(path: "title.value")
path: String! @resolveEntityPath
tags: [Terms]! @resolveEntityReference(field: "field_tags", single: false)
}
If a field requires a custom resolver that is not reusable, the directive just needs to be omitted and the custom resolver implemented as usual.
If we need to create a new directive, it can be declared via a Plugin system.
Similarly, for Metatag, we could declare it like this:
metaTags: [MetaTag] @resolveProperty(path: "metatag")
Then we can provide the type definition of what we want to expose.
type MetaTag {
tag: String!
attributes: MetaTagAttributes!
}
type MetaTagAttributes {
name: String
content: String
property: String
rel: String
href: String
}
As this article is limited to an overview of what currently exists, a complete example based on Directives will be elaborated in another post.
Documentation
Where to start
The Drupal documentation is mostly about v3, for a documentation that is covering both versions, use this Gitbook documentation to get started.
The example modules shipped with the GraphQL module is also a pretty good starting point.
Also to note: the GraphQL module issue queue is on GitHub.
Where to continue
For an exhaustive introduction to the GraphQL module, make sure to watch Building a GraphQL API - Beyond the basics DrupalCon Portland 2022 from Alexander Varwijk (@kingdutch). It comes with this repository.
Open Social and Thunder distributions are inspiring, checking the implementation of both is really worth it to get a deeper understanding of schema building.
I'm really excited about the variety of options that the community is providing, this is showing of versatile GraphQL in Drupal can be and can answer use cases with the freedom of having expressive ways to build the API.