1
votes

Why I need to do this: I'm using graphql + gatsby + contentful. I'm using the rich text field in Contentful which allows to embed object references inside of markdown. I'd like to display the embedded entry.

My query looks like this:

export const pageQuery = graphql`
  query BlogPostBySlug($slug: String!) {
      richTextField {
        content {
            nodeType
            data {
                target {
                    sys {
                        id
                    }
                }
            }
        }
      }
    }
  }
`

The richTextField.content comes back looking like this:

{
  "data": {
    "target": {
      "sys": {
        "id": "5wVh2a6XvySacEioOEMyqQ",
      }
    }
  },
}

The id, "5wVh2a6XvySacEioOEMyqQ", is a reference to another database entry. I can see two potential ways in how this might work:

  1. Some way of telling graphql that the id in the query refers to the other database model and have it autopopulate fields. Is this possible?

  2. Get the results of this query, collect all the ids and construct another query where it searches for entries with those ids. It seems like apollo might be a solution, but it doesn't work well with gatsby. Is this possible?

What is the best way to do this?

Thank you! :)

2

2 Answers

0
votes

The richtext field is fairly new and it looks like this is not resolved yet. Might be good to give the Gatsby folks more information here so that they can progress.

0
votes

You could try using foreign keys to associate the nodes.

In your gatsby-node.js file you'll need to add a key appended with ___NODE with the id of the sys node to link:

exports.sourceNodes = async ({ getNodes, actions, createContentDigest }) => {
  // find all the sys nodes - you'll need to alter this filter to get the right one
  const sysNodes = getNodes().filter(node => node.internal.type === 'File');

  // go through the posts to map the sys nodes
  // these nodes may already exist, so you could filter `getNodes` like above
  const postData = howeverYouGetDataFromContentful();

  postData.forEach(post => {
    const sys = sysNodes.find(embedded => embedded.id === post.data.target.embedded.id);

    // This will create a brand-new node.
    // If your posts already exist, then use `createNodeField` mentioned below
    const node = {
      id: post.id,
      children: [],
      internal: {
        type: 'post',
        mediaType: 'text/json',
        contentDigest: createContentDigest(JSON.stringify(post))
      },
      sys___NODE: sys.id
    };

    actions.createNode(node);
  });
}

If the nodes already exist by the time sourceNodes is called (which they probably will be with Contentful), then you can filter them using getNodes. You'll also probably need to use createNodeField instead of createNode, as mentioned here: https://github.com/gatsbyjs/gatsby/issues/3129#issuecomment-360927436 to avoid recreating nodes.