Search code examples
graphqlgatsbycontentful

Gastby - Add a GraphQL query with parameters in gastby-node.js


Inside gatsby-node.jsI have two queries that gets its data from Contentful. I want to add a new query that loads the data for a particular content based on its slug (a field set in the content model in Contentful).

This is what I have:

  return graphql(
    `
      {
        allContentfulBlogPost {
          edges {
            node {
              id
              slug
            }
          }
        }
        allContentfulCaseStudy(filter: { slug: { ne: "dummy-content" } }) {
          edges {
            node {
              id
              slug
            }
          }
        }
        contentfulCaseStudy(slug: { eq: $slug }) { // <=== Here is the problem
          title
          overview
        }
      }
    `
  )
    .then(result => {
      if (result.errors) {
        console.log("Error retrieving contentful data", result.errors)
      }
    })
    .catch(error => {
      console.log("Error retrieving contentful data", error)
    })
}

So, I want to query that particular case study passing the slug in contentfulCaseStudy(slug: { eq: $slug }) but it doesn't work. It throws this error when I start npm run develop:

 ERROR #85901  GRAPHQL

There was an error in your GraphQL query:

Variable "$slug" is not defined.

File: gatsby-node.js:13:10

Error retrieving contentful data [
  GraphQLError: Variable "$slug" is not defined.
      at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\validation\rules\NoUndefinedVariables.js:38:33)
      at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:345:29)
      at Object.leave (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:395:21)
      at visit (C:\Edited\edited\edited\edited\node_modules\graphql\language\visitor.js:242:26)
      at validate (C:\Edited\edited\edited\edited\node_modules\graphql\validation\validate.js:73:24)
      at GraphQLRunner.validate (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\query\graphql-runner.js:79:44)
      at GraphQLRunner.query (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\query\graphql-runner.js:144:25)
      at C:\Edited\edited\edited\edited\node_modules\gatsby\dist\bootstrap\create-graphql-runner.js:40:19
      at Object.exports.createPages (C:\Edited\edited\edited\edited\gatsby-node.js:13:10)
      at runAPI (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:259:37)
      at Promise.catch.decorateEvent.pluginName (C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:378:15)
      at Promise._execute (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\debuggability.js:384:9)
      at Promise._resolveFromExecutor (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\promise.js:518:18)
      at new Promise (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\promise.js:103:10)
      at C:\Edited\edited\edited\edited\node_modules\gatsby\dist\utils\api-runner-node.js:377:12
      at tryCatcher (C:\Edited\edited\edited\edited\node_modules\bluebird\js\release\util.js:16:23) {
    locations: [ [Object], [Object] ]
  }

Is it possible to request a particular case study passing the slug as parameter? If so, how it's done?


Solution

  • The short answer is that you can't directly. You can filter with a hardcoded parameter, not with a dynamic pre-queried value.

    However, what you are trying to do with $slug is to pass a variable via context API.

    The flow that are you trying to achieve is:

    • Fetch and create pages from Contentful data for allContentfulCaseStudy
    • Use the slug of allContentfulCaseStudy in contentfulCaseStudy to filter your query for each contentfulCaseStudy.

    So, you need to move your contentfulCaseStudy into your template.js modifying your gatsby-node.js like this:

    exports.createPages = async ({ graphql, actions, reporter }) => {
      const { createPage } = actions
    
      const result = await graphql(
        `
          {
            allContentfulCaseStudy(filter: { slug: { ne: "dummy-content" } }) {
              edges {
                node {
                  id
                  slug
                }
              }
            }
          }
        `
      )
    
      if (result.errors) {
        reporter.panicOnBuild(`Error while running GraphQL query.`)
        return
      }
    
      const caseStudyTemplate= path.resolve(`src/templates/case-study.js`)
    
      result.data.allContentfulCaseStudy.edges.forEach(({ node }) => {
        createPage({
          path,
          component: caseStudyTemplate,
          context: {
            slug: node.slug,
          },
        })
      })
    }
    

    Now, in your case-study.js you have available the slug variable since you are passing it via context in your page query. So:

    import React from "react"
    import { graphql } from "gatsby"
    import Layout from "../components/layout"
    
    export default function CaseStudy({ data }) {
      const caseStudy= data.contentfulCaseStudy
      return (
        <Layout>
          <div>
            <h1>{caseStudy.title}</h1>
          </div>
        </Layout>
      )
    }
    
    export const query = graphql`
      query($slug: String!) {
            contentfulCaseStudy(slug: { eq: $slug }) {
              title
              overview
            }
      }
    `
    

    Check your localhost:8000/___graphql playground to see if the nested title and overview are under contentfulCaseStudy or if you need to modify the query structure.

    Further Reading: