How to Query Gatsby Nodes Using the Gatsby GraphQL Schema: A Complete Guide

Gatsby nodes are the fundamental data objects in Gatsby's GraphQL layer, implementing the Node interface with required fields like id and internal, and you query them using the Gatsby GraphQL schema via auto-generated all* collections or the nodeModel API in custom resolvers.

Gatsby nodes form the backbone of the gatsbyjs/gatsby repository's data layer, transforming disparate content sources into a unified GraphQL schema. Understanding how to create, customize, and query these nodes is essential for building high-performance Gatsby sites. This guide examines the actual source code to show you how to leverage the Gatsby GraphQL schema for effective node queries.

What Are Gatsby Nodes?

Gatsby nodes are the core building blocks of Gatsby’s data layer. Every piece of data that Gatsby can query—whether it comes from the file system, a CMS, an API, or is generated by a plugin—is represented as a node. Each node implements the Node interface, which guarantees the presence of a small set of fields (id, parent, children, internal) and makes the node discoverable by Gatsby’s internal NodeModel.

In packages/gatsby/src/schema/types/node-interface.ts, the Node interface is defined with resolvers that delegate to the nodeModel (e.g., context.nodeModel.getNodeById). This ensures that every node in the system, regardless of its source, can be queried through a consistent GraphQL interface.

Gatsby Node Architecture

The following table maps the core architectural concepts to their locations in the Gatsby source code:

Concept Where It Lives in the Source What It Does
Node Interface – the base GraphQL interface all nodes implement packages/gatsby/src/schema/types/node-interface.ts Declares id, parent, children, internal fields and resolves them with the nodeModel
Node type definition – the TypeScript definition of a node object packages/gatsby/index.d.ts Defines NodeInput and Node interfaces that plugins use when creating nodes via actions.createNode
NodeModel – the API that powers all GraphQL resolvers packages/gatsby/src/schema/resolvers.ts Provides findOne, findAll, getNodeById, getNodesByIds, getFieldValue, etc. All GraphQL resolvers delegate to this model
Schema customisation – where you declare additional types or extend existing ones packages/gatsby/src/utils/api-node-docs.ts The createSchemaCustomization API runs before schema generation. You can add type definitions (via createTypes), field extensions, or third‑party schemas
Node creation – API used in sourceNodes packages/gatsby/src/utils/api-node-docs.ts Plugins call actions.createNode to inject data into the graph

The Gatsby GraphQL Schema Data Flow

Understanding how data moves through the Gatsby GraphQL schema helps you query nodes more effectively:

  1. Plugin/source runs sourceNodes (or another node API). It calls actions.createNode to insert raw data.
  2. Gatsby stores the node in the datastore and records its id, parent, children, and internal metadata.
  3. During schema generation, Gatsby reads any schema customization (createSchemaCustomization) and merges the resulting type definitions with the built‑in Node interface.
  4. The NodeModel builds an in‑memory index of all nodes. All GraphQL resolvers (findOne, findAll, etc.) delegate to this model.
  5. When a page or component executes a GraphQL query, Gatsby resolves fields using the NodeModel, automatically handling relationships (parent, children) and custom fields you added.

How to Query Gatsby Nodes Effectively

Because every node implements the Node interface, you can always filter on the common fields (id, parent, children, internal.type) and then request any fields you defined in createTypes or that a source plugin exposed.

Typical patterns:

Goal GraphQL snippet Explanation
Select all nodes of a given type graphql\n{ allMarkdownRemark { nodes { id frontmatter { title } } } }\n all<type> collections are automatically generated for every node type.
Filter by a field defined in createTypes graphql\n{ allProduct(filter: {price: {gt: 20}}) { nodes { id name price } } }\n The field price must be part of the type definition (e.g., via createTypes).
Traverse relationships graphql\n{ allFile(filter: {extension: {eq: "jpg"}}) { nodes { id childImageSharp { gatsbyImageData } } } }\n childImageSharp is a node linked via the parent/children relationship.
Use NodeModel directly in resolvers In a custom resolver you can call context.nodeModel.findOne({ query: { filter: {slug: $slug} } }, {path: context.path}). This is what the built‑in findOne resolver does (see resolvers.ts lines 71‑80).

Practical Examples: Working with Gatsby Nodes

Defining Custom Node Types with createSchemaCustomization

Use the createSchemaCustomization API to define types that implement the Node interface:

// gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  // Define a new type that implements Node
  const typeDefs = `
    type Product implements Node @dontInfer {
      name: String!
      price: Float!
      inStock: Boolean!
    }
  `
  createTypes(typeDefs)
}

Source: createSchemaCustomization docs – packages/gatsby/src/utils/api-node-docs.ts.

Creating Nodes in sourceNodes

After defining the schema, populate it by creating nodes in the sourceNodes API:

exports.sourceNodes = async ({ actions, createNodeId, createContentDigest }) => {
  const { createNode } = actions
  const products = [
    { name: "T‑shirt", price: 19.99, inStock: true },
    { name: "Hat", price: 9.5, inStock: false },
  ]

  products.forEach(product => {
    const node = {
      ...product,
      id: createNodeId(`product-${product.name}`),
      internal: {
        type: "Product",
        contentDigest: createContentDigest(product),
      },
    }
    createNode(node)
  })
}

Reference: createNode usage described in packages/gatsby/src/utils/api-node-docs.ts.

Querying Nodes in Page Components

Because Product implements Node, Gatsby automatically generates the allProduct collection:


# src/pages/products.js

query AllProducts {
  allProduct(filter: {inStock: {eq: true}}) {
    nodes {
      id
      name
      price
    }
  }
}

Writing Custom Resolvers with NodeModel

For advanced use cases, access the NodeModel directly in custom resolvers to query nodes programmatically:

// gatsby-node.js
exports.createResolvers = ({ createResolvers }) => {
  const resolvers = {
    Product: {
      priceWithTax: {
        type: `Float`,
        resolve: (source, args, context) => {
          // `context.nodeModel` is the central API for node queries
          const product = context.nodeModel.getNodeById({ id: source.id })
          return product.price * 1.2 // 20% tax
        },
      },
    },
  }
  createResolvers(resolvers)
}

Implementation details: All built‑in resolvers (findOne, findManyPaginated, etc.) call context.nodeModel (see packages/gatsby/src/schema/resolvers.ts).

Key Source Files in the Gatsby Repository

File Role Direct Link
packages/gatsby/src/schema/types/node-interface.ts Definition and resolution of the Node GraphQL interface (parent/children resolution). https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/types/node-interface.ts
packages/gatsby/index.d.ts TypeScript definition of the Node and NodeInput interfaces used by plugins. https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/index.d.ts#L84-L90
packages/gatsby/src/schema/resolvers.ts Core GraphQL resolvers that delegate to NodeModel (findOne, findAll, pagination, distinct/min/max, etc.). https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/resolvers.ts
packages/gatsby/src/utils/api-node-docs.ts Documentation and examples for the node APIs (createSchemaCustomization, sourceNodes, createNode, etc.). https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/utils/api-node-docs.ts
packages/gatsby/src/schema/context.ts Construction of the nodeModel instance that is passed to every resolver. https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/schema/context.ts

Summary

  • Gatsby nodes are the universal data objects in Gatsby's GraphQL layer, implementing the Node interface with fields like id, parent, children, and internal.
  • The NodeModel (packages/gatsby/src/schema/resolvers.ts) powers all GraphQL queries, providing methods like findOne, findAll, and getNodeById.
  • Use createSchemaCustomization to define types that implement Node, then sourceNodes to populate them via actions.createNode.
  • Query nodes using auto-generated all<Type> collections with filters on both standard Node fields and custom fields.
  • Access context.nodeModel in custom resolvers for programmatic node lookups and advanced data manipulation.

Frequently Asked Questions

What is the difference between a Gatsby node and a regular JavaScript object?

A Gatsby node is a JavaScript object that conforms to the Node interface defined in packages/gatsby/index.d.ts. Unlike regular objects, nodes must include specific metadata fields (id, parent, children, internal) and are indexed by the NodeModel in packages/gatsby/src/schema/resolvers.ts. This indexing allows nodes to be queried via GraphQL and linked through parent-child relationships, whereas plain JavaScript objects exist only in memory and lack GraphQL integration.

How do I filter Gatsby nodes by custom fields in GraphQL?

To filter by custom fields, you must first define those fields using createTypes in createSchemaCustomization (as shown in packages/gatsby/src/utils/api-node-docs.ts). Once the type is defined and nodes are created with those fields, Gatsby automatically generates filter arguments for the all<Type> query. For example, if you define a price field on a Product type, you can query allProduct(filter: {price: {gt: 20}}) to filter nodes where the price is greater than 20.

Can I modify existing Gatsby nodes after they are created?

Nodes should not be mutated directly after creation. Instead, use the createResolvers API to add computed fields or the onCreateNode API to transform nodes during the creation phase. The NodeModel in packages/gatsby/src/schema/resolvers.ts provides read-only access to nodes via methods like getNodeById and findAll. If you need to update node data, you should create a new node with updated content in sourceNodes or use schema customization to extend types with resolver logic rather than mutating the underlying node store.

What is the NodeModel and when should I use it?

The NodeModel is the internal API defined in packages/gatsby/src/schema/resolvers.ts that powers all GraphQL resolvers in Gatsby. It provides methods like findOne, findAll, getNodeById, and getNodesByIds to query the node store. You should use context.nodeModel inside custom resolvers (created via createResolvers) when you need to perform programmatic lookups, implement complex filtering logic, or access nodes that aren't directly available through the standard GraphQL query arguments. This is particularly useful for building custom field resolvers that depend on other nodes in the system.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →