Meertaligheid met Drupal, GraphQL en Gatsby

In eerdere blogs had ik het al over de keuze voor module om Drupal te koppelen aan Gatsby. Er zijn twee smaken, nl. JSON:API en GraphQL. In eerste instantie had ik gekozen voor JSON:API omdat ik toch geen meertaligheid nodig had. Nu wel ;-)

Om die reden moest ik de componenten ombouwen zodat ze meertaligheid ondersteunen. De backend, Drupal 8, was niet zo moeilijk. GraphQL installeren, taal toevoegen, vertalingen instellen, done. De frontend had iets meer voeten in de aarde. De Drupal backend ging GraphQL gebruiken en dat vergt een andere aanpak.

Endpoint

Allereerst het endpoint. Voor JSON:API gebruik je gatsby-source-drupal, waarbij je een apiBase (default: jsonapi) moet opgeven. Voor GraphQL maak je gebruik van de gatsby-source-drupal-graphql plugin waarbij je alleen hoeft aan te geven waar de Drupal omgeving zich bevindt (endpoint wordt automatisch ingevuld). De authenticatie voor JSON:API kan keybased zijn (met de key_auth module), maar voor GraphQL is het beter om de simple_oauth module in te zetten in Drupal. Hiermee maak je via oAuth de koppeling tussen Drupal en Gatsby. Voor het oAuth token is het het beste om een aparte gebruiker en rol aan te maken voor de koppeling met Gatsby. Op deze manier is het ook makkelijk om rechten te distribueren.

Queries

De twee Gatsby plugins hebben andere schema's en dus een andere query structuur. Waar je bij JSON:API terecht kon met een "allNodePage" query, kun je bij GraphQL nodeQuery gebruiken. Een voorbeeld query die ik gebruik 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
          }
        }
      }
    }
  }
}

Hiermee haal je alle nodes op van het type page, article, portfolio en webform. Hiermee komen ook gelijk alle vertalingen (indien aanwezig) mee.

Voor bijv. het uitlijsten van portfolio-items query ik als volgt:

{
    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

Hiermee komen we gelijk bij het stukje "fragments". Met fragments kun je stukken graphql query herbruiken (don't repeat yourself). Naast het gemak wat je hebt van het feit dat je niet per taal de query hoeft aan te passen als je een veld toevoegt, is het ook een essentieel onderdeel om te gebruiken in verband met afbeeldingen. Fragments kun je eigenlijk het beste vergelijken met een JOIN in MySQL

Een fragment definieer je als volgt:

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

Media / afbeeldingen

In het voorgaande fragment zie je nog een fragment ingesloten zitten, nl. om afbeeldingen te laden. Vanuit JSON:API / gatsby_source_drupal is het relatief eenvoudig om afbeelding in de gatsbyImageFile processor te krijgen, maar voor GraphQL moet je iets meer moeite doen. Ik heb het opgelost door in gatsby-node.js een resolver te maken voor het ophalen van de bestanden. Via het fragment kun je dan eenvoudig de afbeelding in een query meenemen.

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_blog