Inferring child and parent fields

In this section, we’ll examine the schema inference Gatsby undertakes to define child fields that have a relationship to their parent field. Consider the example of the File type, for which many transformer plugins exist that convert a file’s contents into a format legible to Gatsby’s data layer. When transformer plugins implement onCreateNode for each File node, this implementation produces File child nodes that carry their own type (e.g., markdownRemark or postsJson).

When Gatsby infers the schema for these child fields, it stores the nodes in Redux by identifying them through ids in each parent’s children field. Then, Gatsby stores those child nodes in Redux as full nodes in their own right. For instance, a File node having two children will be stored in the Redux nodes namespace as follows:

{
  `id1`: { type: `File`, children: [`id2`, `id3`], ...other_fields },
  `id2`: { type: `markdownRemark`, ...other_fields },
  `id3`: { type: `postsJson`, ...other_fields }
}

Gatsby doesn’t store a distinct collection of each child node type. Instead, it stores in Redux a single collection containing all of the children together. One key advantage of this approach is that Gatsby can create a File.children field in GraphQL that returns all children irrespective of type. However, one important disadvantage is that creating fields such as File.childMarkdownRemark and File.childrenPostsJson becomes a more complex process, since no collection of each child node type is available. Gatsby also offers the ability to query a node for its child or children, depending on whether the parent node references one or multiple children of that type.

In Gatsby, upon defining the parent File gqlType, the createNodeFields API will iterate over each unique type of its children and create their respective fields. For example, given a child type named markdownRemark, of which there is only one child node per parent File, Gatsby will create the field childMarkdownRemark. To facilitate queries on File.childMarkdownRemark, we need to write a custom child resolver:

resolve(node, args, context, info)

This resolve function will be invoked whenever we are executing queries for each page, like the following query:

query {
  file( relativePath { eq: "blog/my-blog-post.md" } ) {
    childMarkdownRemark { html }
  }
}

To resolve the File.childMarkdownRemark field, Gatsby will, for each parent File node it resolves, filter over each of its children until it encounters one of type markdownRemark, which is then returned from the resolver function. Because that chil⁠dren value is a collection of identifiers, Gatsby searches for the node by id in the Redux nodes namespace as well.

Before leaving the resolve function’s logic, because Gatsby may be executing this query from within a page, whenever the node changes we need to ensure that the page is rerendered accordingly. As such, when changes in the node are detected, the resolver function calls the createPageDependency function, passing the node identifier and the page: a field available in the context object within the resolve function’s signature.

Finally, once a node is created and designated a child of some parent node, that fact is noted in the child’s parent field, whose value is the parent’s identifier. Then, the GraphQL resolver for this field searches for that parent by that id in Redux and returns it. In the process, it also adds a page dependency through createPageDependency to record that the page on which the query is present has a dependency on the parent node.

NOTE

For more information about how Gatsby handles plain objects or value fields that represent filepaths (such as references to JSON files on disk), consult the Gatsby documentation’s guide to schema inference for file types.


Posted

in

,

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *