Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gatsby-theme-minimal-blog] Additional Post schema fields returning null in allPost query #438

Closed
btroia opened this issue Jul 3, 2020 · 3 comments
Labels
type: question or discussion Issue discussing or asking a question

Comments

@btroia
Copy link

btroia commented Jul 3, 2020

Hi! I am trying to add 2 additional fields (subtitle and featuredImage) to my Post and Page types. I am able to add these new field types in my custom gatsby-node.js file:

exports.createSchemaCustomization = ({ actions, schema }, themeOptions) => {
  const { createTypes } = actions;

  createTypes(`
    interface Post @nodeInterface {
      subtitle: String
      featuredImage: File @fileByRelativePath
    }

    interface Page @nodeInterface {
      subtitle: String
      featuredImage: File @fileByRelativePath
    }

    type MdxPost implements Node & Post {
      subtitle: String
      featuredImage: File @fileByRelativePath
    }

    type MdxPage implements Node & Page {
      subtitle: String
      featuredImage: File @fileByRelativePath
    }
  `);
};

/content/posts/introduction-to-defence-against-the-dark-arts/index.mdx:

---
title: Introduction to "Defence against the Dark Arts"
subtitle: This is my post subtitle
date: 2019-11-07
description: Defence Against the Dark Arts (abbreviated as DADA) is a subject taught at Hogwarts School of Witchcraft and Wizardry and Ilvermorny School of Witchcraft and Wizardry. In this class, students study and learn how to defend themselves against all aspects of the Dark Arts, including dark creatures, curses, hexes and jinxes (dark charms), and duelling.
tags:
  - Tutorial
  - Dark Arts
banner: ./defence-against-the-dark-arts.jpg
featuredImage: ./defence-against-the-dark-arts.jpg
---

I can confirm these fields are then accessible via graphiQL (but they return null since we haven't generated the new nodes yet):

Screen Shot 2020-07-03 at 11 36 01 AM

However, when I try to generate the nodes:

exports.onCreateNode = (
  { node, actions, getNode, createNodeId, createContentDigest },
  themeOptions
) => {
  const { createNode, createParentChildLink } = actions;

  const { postsPath, pagesPath } = withDefaults(themeOptions);

  // Make sure that it's an MDX node
  if (node.internal.type !== `Mdx`) {
    return;
  }
  // Create a source field
  // And grab the sourceInstanceName to differentiate the different sources
  // In this case "postsPath" and "pagesPath"
  const fileNode = getNode(node.parent);
  const source = fileNode.sourceInstanceName;

  // Check for "posts" and create the "Post" type
  if (node.internal.type === `Mdx` && source === postsPath) {
    let modifiedTags;

    if (node.frontmatter.tags) {
      modifiedTags = node.frontmatter.tags.map((tag) => ({
        name: tag,
        slug: kebabCase(tag),
      }));
    } else {
      modifiedTags = null;
    }

    const fieldData = {
      slug: node.frontmatter.slug ? node.frontmatter.slug : undefined,
      title: node.frontmatter.title,
      subtitle: node.frontmatter.subtitle,
      date: node.frontmatter.date,
      tags: modifiedTags,
      banner: node.frontmatter.banner,
      featuredImage: node.frontmatter.featuredImage,
      description: node.frontmatter.description,
    };

    const mdxPostId = createNodeId(`${node.id} >>> MdxPost`);

    createNode({
      ...fieldData,
      // Required fields
      id: mdxPostId,
      parent: node.id,
      children: [],
      internal: {
        type: `MdxPost`,
        contentDigest: createContentDigest(fieldData),
        content: JSON.stringify(fieldData),
        description: `Mdx implementation of the Post interface`,
      },
    });

    createParentChildLink({ parent: node, child: getNode(mdxPostId) });
  }

};

I get the following error:

Error: The plugin "default-site-plugin" created a node of a type owned by another plugin.
          The node type "MdxPage" is owned by "@lekoarts/gatsby-theme-minimal-blog-core".
          If you copy and pasted code from elsewhere, you'll need to pick a new type name
          for your new node(s).

I can eliminate this error by creating a new type name (i.e., MdxPost2):

    const mdxPostId = createNodeId(`${node.id} >>> MdxPost2`);

    createNode({
      ...fieldData,
      // Required fields
      id: mdxPostId,
      parent: node.id,
      children: [],
      internal: {
        type: `MdxPost2`,
        contentDigest: createContentDigest(fieldData),
        content: JSON.stringify(fieldData),
        description: `Mdx implementation of the Post interface`,
      },
    });

But the fields still return null in the allPost query:

Screen Shot 2020-07-03 at 12 32 33 PM

However, I see that a allMdx2Post query was added, which does return the additional data:

Screen Shot 2020-07-03 at 12 43 59 PM

How can I update the allPost query to see this data? Thanks!

@btroia btroia added the type: bug An issue or pull request relating to a bug label Jul 3, 2020
@LekoArts LekoArts added type: question or discussion Issue discussing or asking a question and removed type: bug An issue or pull request relating to a bug labels Jul 3, 2020
@LekoArts
Copy link
Owner

LekoArts commented Jul 3, 2020

Hi, thanks for the issue!

As the error suggests you currently can't override nodes from other plugins/themes. So this is a limitation of Gatsby itself.

You can solve this problem by using a custom field extension/directive for your fields. This is the only code you need in gatsby-node.js:

const addFrontmatterField = (name) => async (source, args, context) => {
  const mdxNode = context.nodeModel.getNodeById({
    id: source.parent,
  })
  return mdxNode.frontmatter[name]
}

exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes, createFieldExtension } = actions

  createFieldExtension({
    name: `addFrontmatterField`,
    args: {
      name: `String!`,
    },
    extend({ name }) {
      return {
        resolve: addFrontmatterField(name),
      }
    },
  })

  createTypes(`
    interface Post @nodeInterface {
      subtitle: String
      featuredImage: File @fileByRelativePath
    }
    
    type MdxPost implements Node & Post {
      subtitle: String @addFrontmatterField(name: "subtitle")
      featuredImage: File @addFrontmatterField(name: "featuredImage") @fileByRelativePath
    }
  `)
}

Since MdxPost is a child of Mdx you get the raw Mdx node with this line:

  const mdxNode = context.nodeModel.getNodeById({
    id: source.parent,
  })

And as Gatsby automatically infers fields, this node contains the new fields from the frontmatter which in turn you then can access.

Important bit for images here: Order matters! You first need to apply the addFrontmatterField directive before applying the fileByRelativePath one.

In a previous issue I've shown accessing the parent in the query (#387), today I came up with this better idea :)

Bildschirmfoto 2020-07-03 um 22 47 31

@btroia
Copy link
Author

btroia commented Jul 3, 2020

@LekoArts Works perfectly, thanks! But in order to add these additional fields to the generated posts/pages (in fieldData), I have to declare a new MdxPost2 type to avoid the error message:

    const fieldData = {
      slug: node.frontmatter.slug ? node.frontmatter.slug : undefined,
      title: node.frontmatter.title,
      subtitle: node.frontmatter.subtitle,
      date: node.frontmatter.date,
      tags: modifiedTags,
      banner: node.frontmatter.banner,
      featuredImage: node.frontmatter.featuredImage,
      description: node.frontmatter.description,
    };

    const mdxPostId = createNodeId(`${node.id} >>> MdxPost2`);

    createNode({
      ...fieldData,
      // Required fields
      id: mdxPostId,
      parent: node.id,
      children: [],
      internal: {
        type: `MdxPost2`,
        contentDigest: createContentDigest(fieldData),
        content: JSON.stringify(fieldData),
        description: `Mdx implementation of the Post interface`,
      },
    });

Do you know if it's possible to use the existing MdxPost type instead of declaring a new/custom MdxPost2 type without getting the previous error I mentioned?

@LekoArts
Copy link
Owner

LekoArts commented Jul 4, 2020

You don’t need the onCreateNode code, my code above is all you need in gatsby-node.js :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: question or discussion Issue discussing or asking a question
Projects
None yet
Development

No branches or pull requests

2 participants