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 } } } } `;