Chris Mitchell Online

Chris blogging about cloud, IT, and programming. What could go wrong?

Drupal CMS Data with Custom GraphQL Schemas

2019-08-27

In this article I am going to setup a custom GraphQL schema that Drupal can use to export CMS data. This article will show how to extend the Drupal 8 GraphQL module and use its examples to build a real world example of a GraphQL server in Drupal 8 while exposing only the data we need in a meaningful way to our front end users.

Prerequisites

I am going to assume that you have a Drupal environment available with the Drupal 8 GraphQL module installed. We will also need access to edit module files. To quickly get up and running with Drupal 8 and GraphQL check out these other articles:

Additionally, it will be helpful to clone the example Drupal module I’ll use later in the article for examples. See the Github project here: https://github.com/motobreath/drupal-graphql.

Seed Data

I’ll create a new content type called Product. Along with the default body field, we’ll add an image field with a machine name field_product_image and a decimal field price with a machine name of field_price.

Viewing the GraphQL product type

And then add content:

Viewing the GraphQL product tshirt

With our product content created we can expose our product data as GraphQL. To do this we’ll need to do a few things: create our GraphQL schema, expose the Drupal data, and create our GraphQL server. First off lets define our schema.

Extend Drupal 8 GraphQL Module

According to the official documentation for the GraphQL module, we should copy the examples directory into our own custom module to extend the examples. At first I thought to myself “why not just modify the examples”? After playing with the schemas, and promptly breaking the code, I found copying the examples out to my own module is a huge help.

I’ve cloned the repo and created a new Drupal module called my_graphql. You can clone the code from my Github repo here, if you haven’t already: https://github.com/motobreath/drupal-graphql.

GraphQL Schema

Creating your schema includes exposing the data that is relevant to your consumers and nothing else. In the my_graphql module, the graphql folder contains the following files:

  • products_extension.base.graphqls
  • products_extension.extension.graphqls
  • products.graphqls

The products.graphqls file is where we define our product data. You’ll see in this file I’ve defined a Product type with id, title, image, and price fields:

schema {
  query: Query
}

type Query {
  product(id: Int!): Product
  products(
    offset: Int = 0
    limit: Int = 10
  ): ProductConnection!
}

type Product {
  id: Int!
  title: String!
  image: String
  price: Float
}

type ProductConnection {
  total: Int!
  items: [Product!]
}

The other two extension files are used to define what queries and pages look like in GraphQL, for these I’ve only changed the example names to match my product content type names.

Expose Product Data

Now we need to let Drupal know how to expose our product data. We can do this by using the Schema plugin provided by the GraphQL module. In the folder my_graphql/src/Plugin/GraphQL/Schema folder, you’ll see my ProductsSchema.php file, and in there the addProductFields method where I use the ResolverRegistry and ResolverBuilder objects to pull my product content data from Drupal.

For my schema I am proving id, title, price, and image:

<?php
/**
   * @param \Drupal\graphql\GraphQL\ResolverRegistry $registry
   * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
   */
  protected function addProductFields(ResolverRegistry $registry, ResolverBuilder $builder) {
    $registry->addFieldResolver('Product', 'id',
      $builder->produce('entity_id')
        ->map('entity', $builder->fromParent())
    );
    $registry->addFieldResolver('Product', 'title',
      $builder->compose(
        $builder->produce('entity_label')
          ->map('entity', $builder->fromParent()),
        $builder->produce('uppercase')
          ->map('string', $builder->fromParent())
      )
    );
    $registry->addFieldResolver('Product', 'price',
    $builder->produce('property_path')
      ->map('type', $builder->fromValue('entity:node'))
      ->map('value', $builder->fromParent())
      ->map('path', $builder->fromValue('field_price.value'))
    );
    
    $registry->addFieldResolver('Product', 'image',
      $builder->compose(
       $builder->produce('property_path')
         ->map('type', $builder->fromValue('entity:node'))
         ->map('value', $builder->fromParent())
         ->map('path', $builder->fromValue('field_product_image.entity')),            
       $builder->produce("image_url")
         ->map('entity',$builder->fromParent()))
    );
  }

The Drupal 8 GraphQL module provides many more ways to pull your Drupal data, see the documentation on resolvers for more information: https://drupal-graphql.gitbook.io/graphql/v/8.x-4.x/data-producers/built-in.

Data Producer

We also need to define a data producer for Drupal. This file is defined in my_graphql/src/Plugin/GraphQL/DataProducer in the file QueryProducts.php. The class QueryProducts extends the Drupal 8 GraphQL DataProducerPluginBase class and essentially wires up the GraphQL queries to the schema we provided earlier. The example class QueryArticles has the necissary methods already defined, so I just changed the class to use my products schema instead of articles.

Create GraphQL Endpoint

With our schema and data producers in place for our product data we can create the GraphQL server. We can do this in the admin UI, visit Configuration -> Graph QL or the url /admin/config/graphql and click the +Create Server button:

Create GraphQL Endpoint

For the server details:

  • Label of Products
  • select Products schema for the Schema
  • Endpoint will be /graphql/products
  • Uncheck Allow query batching
  • Uncheck Enable caching
  • Check Enable debugging

Save your changes when done. When back on the server screen you will see your new Products server, with an Edit button under Operations. Click the down arrow next to Edit to see more options including Explorer and Voyager. Select Explorer to bring up the GraphQL query explorer.

Create GraphQL Server

GraphQL Query

We can now query our products! To return all products with a count, use the following query, and click the play button:

{
	products{
    
   total
   items{    
      id
      title 
      price
      image
    
   }
  }
  	
}

If you set up your Drupal content similar to mine, you should see the data returned:

{
  "data": {
    "products": {
      "total": 1,
      "items": [
        {
          "id": 1,
          "title": "AWESOME T-SHIRT",
          "price": 26.99,
          "image": "http://localhost:8099/sites/default/files/2019-08/tshirt_2.jpeg"
        }
      ]
    }
  }
}

You can use the query explorer to help write your GraphQL queries. The Documentation Explorer can also help define what queries you can write, based on your schema. GraphQL Explorer

Conclusion

Having our Drupal data exposed as meaningful GraphQL for end users to consume is a huge advantage. In previous versions of the Drupal 8 GraphQL module you could only expose the data in Drupal terms, for example field_price but if our consumers weren’t familiar with this nomenclature, the GraphQL would be overcomplicated and unusable.

Sources

comments powered by Disqus