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

> Learn to query Gatsby nodes using the Gatsby GraphQL schema. Discover how to effectively access your site's data with our complete guide.

- Repository: [Gatsby/gatsby](https://github.com/gatsbyjs/gatsby)
- Tags: how-to-guide
- Published: 2026-03-06

---

**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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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:

```javascript
// 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`](https://github.com/gatsbyjs/gatsby/blob/main/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:

```javascript
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`](https://github.com/gatsbyjs/gatsby/blob/main/packages/gatsby/src/utils/api-node-docs.ts).

### Querying Nodes in Page Components

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

```graphql

# 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:

```javascript
// 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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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`](https://github.com/gatsbyjs/gatsby/blob/main/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.