Multilingual Drupal, GraphQL and Gatsby

In previous blogs I mentioned the options you have when linking Drupal to Gatsby. There are two flavours, namely JSON:API and GraphQL. At first I chose JSON:API because I didn't need multilingual support. I do now ;-)

For that exact reason I need to redesign my React components to support multiple languages. The backend, Drupal 8, wasn't that hard to change. Install GraphQL, add a language, configure tranlations and there you go. The frontend need a bit more TLC. The Drupal backend was going to use GraphQL and that requires a different approach.

Endpoint

First the endpoint. For JSON:API you need the gatsby-source-drupal plugin, where you need to set the apiBase (default: jsonapi). For GraphQL you use a different gatsby plugin, gatsby-source-drupal-graphql where you only need to tell what the domain of the Drupal backend is (and the rest is done automatically). Authentication with JSON:API can be keybased (with the key_auth module), but for GraphQL it's better to use the simple_oauth module in Drupal. With this module is easy to create an oAuth link between Drupal and Gatsby. To get the oAuth token, it's best to create a sepearte user and userrole. This way you can better manage permissions.

Queries

The two different Gatsby plugins use different schemas and also a different query base. Where you could use an "allNodePage" query in JSON:API, you need to use "nodeQuery" with GraphQL. An example of the query I use in gatsby-node.js:

{
  drupal {
    nodes: nodeQuery(
      filter: {
        conditions: [
          { operator: EQUAL, field: "status", value: "1" }
          { operator: IN, field: "type", value: ["page", "article", "portfolio", "webform"] }
        ]
      },
      limit: 1000000
    ) {
    entities {
      entityTranslations {
        entityId
        entityBundle
        entityLanguage {
          id
        }
        ... on Drupal_Node {
          path {
            alias
          }
        }
      }
    }
  }
}

With this query you can retrieve all published nodes of the type page, article, portfolio and webform. You'll also get all the translations (if present).

For listing all portfolio-items I use the following query:

{
    drupal {
        nodeQuery(
            filter: {
                conditions: [
                    {operator: EQUAL, field: "status", value: "1"}
                    {operator: EQUAL, field: "type", value: "portfolio"}
                ]
            },
            limit: 1000,
            sort: {field: "field_timeline.end_value", direction: DESC}
        ) {
            entities {
                nl: entityTranslation(language: NL) {
                    ... on Drupal_NodePortfolio {
                        ... PortfolioFields
                    }
                }
                en: entityTranslation(language: EN) {
                    ... on Drupal_NodePortfolio {
                        ... PortfolioFields
                    }
                }
            }
        }
    }
}

Fragments

With that we get to "fragments". Fragments can best be compared to JOIN queries in MySQL. With fragments you can reuse parts of a graphql query (which suits the "Don't Repeay Yourself" philosophy very well). It's not only very easy to add fields to the query, it's also essential to use when retrieving files.

A fragment is defined as follows:

export const PortfolioFields = graphql`
fragment PortfolioFields on Drupal_NodePortfolio {
    nid
    title
    body {
        summary
        processed
    }
    fieldMediaImage {
        entity {
            ... on Drupal_MediaImage {
                ...MediaImage
            }
        }
    }
}
`;

Media / images

In the aforementioned fragment you'll also see a nested fragment, to retrieve the images. With JSON:API / gatsby_source_drupal it's relatively eay to query for an image from Drupal, but GraphQL / gatsby_source_drupal_graphql won't automatically transfer the images. I solved it by adding a resolver to retrieve the files. With the fragment you can then query for the images.

gatsby-node.js:

exports.createResolvers = ({
    actions,
    getCache,
    createNodeId,
    createResolvers,
}) => {
    const { createNode } = actions
    createResolvers({
        Drupal_MediaImage: {
            gatsbyImageFile: {
                type: `File`,
                resolve(source) {
                    return createRemoteFileNode({
                        url: source.fieldMediaImage.url,
                        getCache,
                        createNode,
                        createNodeId,
                    })
                },
            },
        },
    })
}

Fragment:

export const MediaImage = graphql`
  fragment MediaImage on Drupal_MediaImage {
    fieldMediaImage {
      url
      alt
    }
    gatsbyImageFile {
      childImageSharp {
        fluid {
          originalName
          ...GatsbyImageSharpFluid
        }
      }
    }
  }
`;
Back to blog overview
Ik wil graag weten hoeveel bezoeker ik heb en wat ze doen op mijn site (via Google Analytics). Om dit te doen gebruik ik cookies. De data wordt anoniem opgeslagen en ik respecteer de Do Not Track voorkeuren van uw browser. Wilt u een cookie van mij?