title: Connecting two gatsby nodes
body: <p>So, I'm using the <a href="http://gatsby-mdx.netlify.com/" rel="nofollow noreferrer">gatsby-mdx</a> plugin to create a site from MDX files. I want to create an association between the SitePage object and the Mdx object so that I can do one graphQL query of the SitePage edges in order to construct a site navigation.</p>
  
  <p>Much of my code is in TypeScript, so ignore any type annotations if you're wondering WTF those are.</p>
  
  <h1>Things I've tried</h1>
  
  <h2>Using Fields</h2>
  
  <p>My first thought was to use the <a href="https://www.gatsbyjs.org/docs/node-apis/#onCreateNode" rel="nofollow noreferrer"><code>onCreateNode</code></a> API, grab the MDX node, and add it to the SitePage using the <code>createNodeField</code> action. That all works great, B-U-T the gatsby-mdx plugin <a href="https://github.com/ChristopherBiscardi/gatsby-mdx/blob/master/packages/gatsby-mdx/gatsby/extend-node-type.js" rel="nofollow noreferrer">adds a bunch of other info to their node</a> later using the <a href="https://www.gatsbyjs.org/docs/node-apis/#setFieldsOnGraphQLNodeType" rel="nofollow noreferrer"><code>setFieldsOnGraphQLNodeType</code></a> API (which occurs <em>after</em> the <code>onCreateNode</code> API).  I want those fields (such as frontmatter and tableOfContents) to be available in later graphql queries, but they aren't using this method.</p>
  
  <h2>Implementing my own <code>setFieldsOnGraphQLNodeType</code></h2>
  
  <p>I figured I could just extend the SitePage object the same way gatsby-mdx was extending the Mdx node.</p>
  
  <p>The key problem I ran into here was that I couldn't figure out how to create the Mdx GraphQL node type.</p>
  
  <pre><code>export const setFieldsOnGraphQLNodeType = ({type, actions, getNodes}: any, pluginOptions: any) =&gt; {
      if (type.name === "SitePage") {
          const {createParentChildLink} = actions
          return new Promise((resolve) =&gt; {
              return resolve({
                  "childMdx": {
                      type: new GraphQLObjectType({
                          name: 'Mdx'
                      }),
                      async resolve(sitePageNode: any) {
                          const allNodes = getNodes()
                          if (sitePageNode.component &amp;&amp;
                              (sitePageNode.component.endsWith(".mdx") || sitePageNode.component === DefaultLayout)
                          ) {
                              const associatedMdx = allNodes.find((mdxNode: any) =&gt;
                                  mdxNode.internal.type === 'Mdx' &amp;&amp; mdxNode.fileAbsolutePath === sitePageNode.component
                              )
                              if (associatedMdx) {
                                  console.log("Found associated MDX node", associatedMdx.id)
                                  console.log("Adding it to the sitepage node", sitePageNode.id)
                                  return associatedMdx
                              }
                          }
                      }
                  }
              })
          })
      }
      return {}
  }
  </code></pre>
  
  <p>I also tried simply passing the type as a String ('Mdx'), but that failed too.</p>
  
  <h2>Using Parent-Child Links</h2>
  
  <p>That plugin creates a parent-child link between the File node and the parsed MDX node in the <code>onCreateNode</code> API, using the <a href="https://www.gatsbyjs.org/docs/actions/#createParentChildLink" rel="nofollow noreferrer">createParentChildLink</a> action (<a href="https://github.com/ChristopherBiscardi/gatsby-mdx/blob/master/packages/gatsby-mdx/gatsby/on-create-node.js#L55" rel="nofollow noreferrer">source</a>).</p>
  
  <p>I tried implementing that...</p>
  
  <pre><code>export const onCreateNode = ({node, actions, getNodes}: OnCreateNodeArgument) =&gt; {
      const {createParentChildLink} = actions
      const allNodes = getNodes()
      if (node.internal &amp;&amp; node.internal.type === 'SitePage' &amp;&amp; node.component &amp;&amp;
          (node.component.endsWith(".mdx") || node.component === DefaultLayout)
      ) {
          const associatedMdx = allNodes.find((mdxNode: any) =&gt;
              mdxNode &amp;&amp; mdxNode.internal &amp;&amp; mdxNode.internal.type === 'Mdx' &amp;&amp;
                  (mdxNode.fileAbsolutePath === node.component || mdxNode.fileAbsolutePath === node.context.fileAbsolutePath)
          )
          if (associatedMdx) {
              console.log("Found associated MDX node", associatedMdx.id)
              console.log("Adding it to the sitepage node as a child", node.id)
              createParentChildLink({parent: node, child: associatedMdx})
          }
      }
  }
  </code></pre>
  
  <p>At first, that appears to succeed, but the <a href="https://github.com/ChristopherBiscardi/gatsby-mdx/blob/master/packages/gatsby-mdx/gatsby/extend-node-type.js#L192" rel="nofollow noreferrer"><code>tableOfContents</code> property</a> that gatsby-mdx adds to the Mdx node still isn't available in a graphQL query like:</p>
  
  <pre><code>{
      allSitePage(filter: {fields: {childMdx: {id: {ne: null}}}}) {
          edges {
              node {
                  path
                  fields{
                      childMdx {
                          tableOfContents
                          fileAbsolutePath
                          frontmatter {
                              title
                          }
                      }
                  }
                  context {
                      roughFilePath
                      id
                  }
              }
          }
      }
  }
  
  </code></pre>
  
  <h1>Other (possibly irrelevant) info</h1>
  
  <p>I'm <a href="https://gatsby-mdx.netlify.com/guides/programmatically-creating-pages" rel="nofollow noreferrer">creating some pages programmatically</a> in gatsby-node.js.</p>
  
  <p>I've seen a suggestion for similar use cases to use <a href="https://www.gatsbyjs.org/docs/gatsby-config/#mapping-node-types" rel="nofollow noreferrer">node type mappings</a>, but I since my mapping between the SitePage &amp; the MDX object requires a bit of finesse (specifically, reading some things from siteMetadata and doing a string comparison), I don't think that will work for my use case.</p>