Picture
Calvin Tyndall Drupal Developer Follow
November 18, 2020

At BADCamp 2019 I attended Michael Schmid’s presentation on decoupled web site building and hosting. There, I learned about the GraphQL module that's been developed to do decoupled Drupal, instead of JSON:API. When I first started experimenting with Gatsby I decided to try it, not really knowing anything about GraphQL at the time. This made my experience trickier than it needed to be. As it turned out, all the tutorials and examples I could find for Drupal + Gatsby had query examples for gatsby_source_drupal with JSON:API. Once I got my head around GraphQL though, things were pretty slick.

In Gatsby you use GraphQL to query data from your backend, the source plugin you use will allow you to access data from your source API. The gatsby_source_drupal plugin pulls data from Drupal JSON:API so it’s queryable with GraphQL inside your Gatsby app, while gatsby_source_graphql is just a wrapper around any backend GraphQL schema. This means we get access to the schema in the GraphQL 3 module for Drupal instead of the one defined in the gatsby_source_drupal plugin.

Let's compare some common query examples from both of these methods.

The queries here are kept simple to focus on specific elements and context.

Examples

Example 1: Querying a List of Nodes

Lets get a list of 3 nodes of type Article for a link list. Make sure they are published and sorted by sticky and changed.

JSON:API allNodeArticle

Query


query {
  allNodeArticle(
    filter: {
      status: {
        eq: true
      }
    }
    sort: {
      fields: [sticky, changed]
      order: [DESC, DESC]
    }
    skip: 0
    limit: 3
  ) {
    edges {
      node {
        drupal_internal__nid
        title
        path {
          alias
        }
      }
    }
  }
}

GraphQL nodeQuery

Query


query {
  drupal {
    nodeQuery(
      filter: {
        conditions: [
          { field: "status", value: "1" }
          { field: "type", value: "article" }
        ]
      }
      sort: [
        { field: "sticky", direction: DESC }
        { field: "changed", direction: DESC }
      ]
      offset: 0
      limit: 3
    ) {
      entities {
        ... on Drupal_NodeArticle {
          nid
          title
          path {
            alias
          }
        }
      }
    }
  }
}

The main difference I see in the query is how the filter and sort parameters work. With gatsby_source_drupal we have a set of filters for fields defined on the entity type; id, langcode, status, title, etc.

With gatasby_source_graphql and the GrapqhQL module I'm able to use "conditions" the way I would with EntityQuery in Drupal, working in PHP. For instance, I could filter my list of nodes by taxonomy term by adding a condition for that field.


...
    nodeQuery(
      filter: {
        conditions: [
          { field: "status", value: "1" }
          { field: "type", value: "article" }
          { field: "field_tags.entity.name", value: "sailboat"}
        ]
      }
    }
...

I could do this in gatsby_source_drupal as well, but it looks like:


...
  allNodeArticle(
    filter: {
      status: {
        eq: true
      }
      relationships: {
        field_tags: {
          elemMatch: {
            name: {
              eq: "sailboat"
            }
          }
        }
      }
    }
...

It might just be because I come from the backend first that I find the nodeQuery way more intuitive. I like that you can customize the query with conditions, conjunctions and groups. https://drupal-graphql.gitbook.io/graphql/queries/filters

Example 2: Query a Node With Nested Paragraphs

Sometimes I use the Drupal Paragraphs module to make flexible columns of text. In this very simple example I've named the paragraphs field field_components and there are 2 types of paragraphs:

  • Columns: has one field field_components, another paragraph reference field.
  • Text: has field_heading and field_text

On my Article content type I added a paragraphs field called "field_components" as well. I can create an article with a "Columns" paragraph and inside the "Columns" paragraph I can add "Text" paragraphs. The Columns paragraph becomes a wrapper and can be styled to cause the nested Text paragraphs to layout as columns.

In the query, I've aliased the paragraph fields to "column_wrapper" and "columns", so it's a little clearer than field_components in the data.

JSON:API nodeArticle

Query


query($id: Int!) {
  nodeArticle(drupal_internal__nid: { eq: $id }) {
    relationships {
      column_wrapper: field_components {
        ... on paragraph__columns {
          relationships {
            columns: field_components {
              ... on paragraph__text {
                field_heading {
                  processed
                }
                field_text {
                  processed
                }
              }
            }
          }
        }
      }
    }
  }
}

GraphQL nodeById

Query


query($id: String!) {
  drupal {
    nodeById (
      id: $id
    ) {
      ... on Drupal_NodeArticle {
        column_wrapper: fieldComponents {
          entity {
            ... on Drupal_ParagraphColumns {
              columns: fieldComponents {
                entity {
                  ... on Drupal_ParagraphText {
                    fieldHeading {
                      processed
                    }
                    fieldText {
                      processed
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

You can see some differences in the way the schema identifies the fields. One uses relationships and field_components while the other uses fieldComponents and entity

JSON:API: nodeArticle has arguments for all the node fields; langcode, status, created, etc. while the GraphQL nodeById has only id and langcode. But if you're not looking for a specific node id and want to filter on something else, use the more flexible nodeQuery.

Example 3: Image Styles

In my previous post I talked about using Drupal to process and serve derivative images instead of having Gatsby process them all at build time, which can take a very long time as the number of posts/images grows.

I compared the two source queries for this as well, and I think the differences are significant in this case. You can review that post for the details but I'll briefly summarise.

The goal was to load a set of image styles from an image field. To do this with gatsby_source_drupaland the JSON:API requires an additional Drupal contrib module to expose the images styles. The GraphQL module just handles it with derivative(style: [name]) on the field. Which can be aliased to query all the styles you wish.


...
  fieldImage {
    small: derivative(style: small) {
      width
      url
    }
    medium: derivative(style: medium) {
      width
      url
    }
    ...

The resulting data from GraphQL was easier to process, while the structure of the data from the JSON:API version kind of didn't make sense and required some extra massaging.

Final Thoughts

In general the gatsby-source-drupal plugin gives us queries in the form of "entityBundle" or "allEntityBundle" like the following:

  • nodeArticle, allNodeArticle
  • paragraphColumns, allParagraphColumns
  • taxonomyTermTags, allTaxonomyTermTags

These take arguments mapped to the entity fields.

The GraphQL module instead provides us with entity queries on each entity type that we can use just like an EntityQuery in Drupal PHP, as well as entityById and entityRevsionById.

  • queryNode, nodeById, nodeRevisionById
  • queryUser, userById
  • blockContentQuery, blockContentById, blockContentRevisionById

So far I've been able to do the things I want with either solution, but I find myself favoring GraphQL module. I assume that's from my backend roots. Lucky for us the GraphQL Explorer makes it all pretty easy to explore. Please let me know in the comments if I've said anything preposterous, or if you have any additional insights. Thanks for reading!

"Code like nobody is watching... but then refactor. ;)"