Skip to content Skip to sidebar Skip to footer

Graphql Query Callbacks For Gatsby.js

In the Contentful CMS, I have two different content-types: BigCaseStudy and BigCaseStudySection. To get this content to appear in my Gatsby 2.x site, my thinking was: Do query 1,

Solution 1:

I ran into this challenge too and couldn't find a good solution to accomplish that (although I wasn't using Contentful), but I did work past it and think I can help. You'll need to shift your thinking a bit.

Basically, GraphQL isn't really meant to query for the data you need to run another query. It's more of a 'ask for what you need' sort of tool. GraphQL wants to run a single query for exactly what you need.

The parameter you need for your query actually comes from your gatsby-node.js file. Specifically, the context property of createPages()(a Node API that gatsby makes available).

Is that enough to get you pointed in the right direction? If you need a bit more of a hand, then there are two things I need to know: 1. A little more context around what you're trying to accomplish. What is the specific data you want available to the end user? 2. What your gatsby-node.js file looks like.

Solution 2:

Short answer: you don't do callbacks with GraphQL. You do one query that gets everything you need all at once.

Longer answer: I had to reconstruct how the gatsby-node.js file fetched Contentful content and then filtered through it. In Gatsby, you want to set up the queries in gatsby-node.js to go fetch everything from your data source because it's a static site generator. It's architecture brings all that data in and then parses it out accordingly.

The GraphQL query from my original question was fine. I changed .then() of the promise to use .filter() on my results, comparing the relationship field of the child nodes to the id of the parent nodes.

gatsby-node.js:

// Set Gatsby path up to be used by .createPagesconst path = require('path')

// Using Node's module export, Gatsby adds in a createPages factory exports.createPages = ({ graphql, actions }) => {

  // We setup the createPage function takes the data from the actions objectconst { createPage } = actions

  // Setup a promise to build pages from contentful data model for bigCaseStudiesreturnnewPromise((resolve, reject) => {

    // Setup destination component for the dataconst bigCaseStudyComponent = path.resolve('src/components/BigCaseStudy/bigcasestudy.js')

    resolve(
      graphql(`
        {
          allContentfulBigCaseStudy {
            edges {
              node { 
                id
                caseStudySlug
                caseStudyTitle
                caseStudySubtitle
                caseStudyIntroTitle
                caseStudyIntro {
                  caseStudyIntro
                }
                caseStudyLink
                caseStudyHero {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }                  
                }
              }
            }
          }
          allContentfulBigCaseStudySection {
            edges {
              node {
                title
                order
                images {
                  fixed {
                    width
                    height
                    src
                    srcSet
                  }
                }
                bigCaseStudyReference {
                  id
                }
                body {
                  body
                }
                stats {
                  stat1 {
                    word
                    number
                  }
                  stat2 {
                    word
                    number
                  }
                  stat3 {
                    word
                    number
                  }
                  stat4 {
                    word
                    number
                  } 
                }
                id
              }
            }
          }
        }
      `).then((result) => {

        // Now we loop over however many caseStudies Contentful sent back
        result.data.allContentfulBigCaseStudy.edges.forEach((caseStudy) => {
          let matchedCaseStudySections = result.data.allContentfulBigCaseStudySection.edges.filter(
            caseStudySection => 
              caseStudySection.node.bigCaseStudyReference.id === caseStudy.node.id 
          )

          createPage ({
            path: `/work/${caseStudy.node.caseStudySlug}`,
            component: bigCaseStudyComponent,
            context: {
              id: caseStudy.node.id,
              slug: caseStudy.node.caseStudySlug,
              title: caseStudy.node.caseStudyTitle,
              subtitle: caseStudy.node.caseStudySubtitle,
              hero: caseStudy.node.caseStudyHero,
              introTitle: caseStudy.node.caseStudyIntroTitle,
              intro: caseStudy.node.caseStudyIntro.caseStudyIntro,
              link: caseStudy.node.caseStudyLink,
              caseStudySection: matchedCaseStudySections.node
            }
          })

        })
      })

      // This is the error handling for the calls
      .catch((errors) => {
        console.log(errors)
        reject(errors)
      })

    ) // close resolve handler
  }) // close promise
}

Once you've set this up, the createPage part of Gatsby Node API sends the parent and all of it's nodes over to the component param you set.

Inside of my component, I can now make a GraphQL query for all children nodes. That now returns what I want and conforms to the idea that GraphQL makes one request instead of multiple like I was trying to do. The only tricky part is that you have to use .map() in the render part of the component to loop over all the child nodes sent back from Contentful.

bigcasestudy.js component:

importReactfrom'react'importPropTypesfrom'prop-types'import styled from'styled-components'import { graphql } from'gatsby'importImgfrom'gatsby-image'importLayoutfrom'../Layout/layout'/** 
 * Hero Section
 */constHeroContainer = styled.header`
  align-items: center;
  background-image: url(${ props => props.bgImgSrc });
  background-position: center center;
  background-size: cover;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: calc(100vh - 128px);
`constHeroTitle = styled.h1`
  color: #fff;
  font-size: 70px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 15px;
`constHeroSubtitle = styled.h2`
  color: #fff;
  font-size: 24px;
  font-weight: 300;
  letter-spacing: 5px;
  text-transform: uppercase;
`/** 
 * Intro Section
 */constIntroBG = styled.section`
  background-color: ${ props => props.theme.lightGray };
  padding: 50px 0;
`constIntroContainer = styled.div`
  padding: 25px;
  margin: 0 auto;
  max-width: ${ props => props.theme.sm };

  @media (min-width: ${ props => props.theme.sm }) {
    padding: 50px 0;
  }
`constIntroTitle = styled.h2`
  font-size: 50px;
  font-weight: 700;
  letter-spacing: 2px;
  margin-bottom: 45px;
  text-align: center;
`constIntroText = styled.p`
  font-size: 22px;
  line-spacing: 4;
  text-align: center;
`constIntroButton = styled.a`
  background-color: #fff;
  color: ${ props => props.theme.darkGray };
  border: 1px solid ${ props => props.theme.mediumGray };
  border-radius: 25px;
  display: block;
  font-size: 16px;
  letter-spacing: 5px;
  margin: 30px auto;
  padding: 15px 45px;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  width: 300px;
`// BigCaseStudy ComponentclassBigCaseStudyextendsReact.Component {
  render() {

    // Destructure Case Study Intro stuffconst { 
      caseStudyHero, 
      caseStudyIntro, 
      caseStudyIntroTitle, 
      caseStudyLink, 
      caseStudySubtitle, 
      caseStudyTitle 
    } = this.props.data.contentfulBigCaseStudy// Setup references to Case Study Sections, destructure BigCaseStudySection objectconst caseStudySections = this.props.data.allContentfulBigCaseStudySection.edges.map( 
      (currentSection) => {
        return currentSection.node
      }
    )

    // Case Study Section can be in any order, so we need to sort them outconst caseStudySectionsSorted = caseStudySections.sort( (firstItem, secondItem) => {
      return firstItem.order > secondItem.order ? 1 : -1
    })

    console.log(caseStudySectionsSorted)

    return (
      <Layout><HeroContainerbgImgSrc={caseStudyHero.fixed.src }><HeroTitle>{ caseStudyTitle }</HeroTitle><HeroSubtitle>{ caseStudySubtitle }</HeroSubtitle></HeroContainer><IntroBG><IntroContainer><IntroTitle>{ caseStudyIntroTitle }</IntroTitle><IntroText>{ caseStudyIntro.caseStudyIntro }</IntroText></IntroContainer><IntroButtonhref={caseStudyLink } target="_blank"rel="noopener noreferrer">
            Visit the site >
          </IntroButton></IntroBG>
        {
          caseStudySectionsSorted.map( (caseStudySection, index) => {
            return <IntroTitlekey={index }>{ caseStudySection.title }</IntroTitle>
          })
        }
      </Layout>
    )
  }
}

// Confirm data coming out of contentful call is an objectBigCaseStudy.propTypes = {
  data: PropTypes.object.isRequired
}

// Export componentexportdefaultBigCaseStudy// Do call for the page data// This needs to mirror how you've set up the dynamic createPage function data in gatsby-node.jsexportconstBigCaseStudyQuery = graphql`
  query BigCaseStudyQuery {
    contentfulBigCaseStudy {
      id
      caseStudyTitle
      caseStudySubtitle
      caseStudyIntroTitle
      caseStudyIntro {
        caseStudyIntro
      }
      caseStudyLink
      caseStudyHero {
        fixed {
          width
          height
          src
          srcSet
        }                  
      }
    }
    allContentfulBigCaseStudySection {
      edges {
        node {
          title
          order
          images {
            fixed {
              width
              height
              src
              srcSet
            }
          }
          bigCaseStudyReference {
            id
          }
          body {
            body
          }
          stats {
            stat1 {
              word
              number
            }
            stat2 {
              word
              number
            }
            stat3 {
              word
              number
            }
            stat4 {
              word
              number
            } 
          }
          id
        }
      }
    }
  }
`

H/t: thanks to @taylor-krusen for rearranging how I was approaching this problem.

Post a Comment for "Graphql Query Callbacks For Gatsby.js"