Server-side APIs for plugin development, page creation, data sourcing, and build-time customization through the gatsby-node.js file.
Main interface for build-time plugin APIs that control data sourcing, page creation, schema customization, and build configuration.
/**
* Main interface for gatsby-node.js plugin APIs
*/
interface GatsbyNode {
/** Plugin initialization */
onPluginInit?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** Before Gatsby initialization */
onPreInit?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** Before bootstrap phase */
onPreBootstrap?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** After bootstrap phase */
onPostBootstrap?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** Before build phase */
onPreBuild?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** After build phase */
onPostBuild?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** Before query extraction */
onPreExtractQueries?: (args: ParentSpanPluginArgs) => Promise<void> | void;
/** Create pages programmatically */
createPages?: (args: CreatePagesArgs) => Promise<void> | void;
/** Create pages with state management */
createPagesStatefully?: (args: CreatePagesArgs) => Promise<void> | void;
/** Modify created pages */
onCreatePage?: (args: CreatePageArgs) => Promise<void> | void;
/** Source nodes from external systems */
sourceNodes?: (args: SourceNodesArgs) => Promise<void> | void;
/** Process created nodes */
onCreateNode?: (args: CreateNodeArgs) => Promise<void> | void;
/** Filter node processing */
shouldOnCreateNode?: (args: {
node: Node;
}, pluginOptions?: PluginOptions) => boolean;
/** Customize GraphQL schema */
createSchemaCustomization?: (args: CreateSchemaCustomizationArgs) => Promise<void> | void;
/** Add GraphQL resolvers */
createResolvers?: (args: CreateResolversArgs) => Promise<void> | void;
/** Add fields to GraphQL node types */
setFieldsOnGraphQLNodeType?: (args: SetFieldsOnGraphQLNodeTypeArgs) => object | Promise<object>;
/** Modify Babel configuration */
onCreateBabelConfig?: (args: CreateBabelConfigArgs) => Promise<void> | void;
/** Modify Webpack configuration */
onCreateWebpackConfig?: (args: CreateWebpackConfigArgs) => Promise<void> | void;
/** Configure development server */
onCreateDevServer?: (args: CreateDevServerArgs) => Promise<void> | void;
/** Preprocess source files */
preprocessSource?: (args: PreprocessSourceArgs) => Promise<string> | string;
/** Add resolvable file extensions */
resolvableExtensions?: (args: ParentSpanPluginArgs) => string[] | Promise<string[]>;
/** Plugin options validation schema */
pluginOptionsSchema?: (args: PluginOptionsSchemaArgs) => object;
}Create and manage pages programmatically during the build process.
/**
* Create pages programmatically
*/
interface CreatePagesArgs extends ParentSpanPluginArgs {
graphql: GraphQLFunction;
actions: Actions;
reporter: Reporter;
}
/**
* GraphQL function for querying data during page creation
*/
type GraphQLFunction = <TData = any>(
query: string,
variables?: Record<string, any>
) => Promise<{
errors?: any;
data?: TData;
}>;Usage Examples:
// gatsby-node.js
const path = require("path");
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions;
// Query for blog posts
const result = await graphql(`
{
allMarkdownRemark {
edges {
node {
id
frontmatter {
slug
title
}
html
}
}
}
}
`);
if (result.errors) {
reporter.panicOnBuild("Error loading blog posts", result.errors);
return;
}
// Create blog post pages
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
createPage({
path: `/blog/${node.frontmatter.slug}`,
component: path.resolve("./src/templates/blog-post.js"),
context: {
id: node.id,
slug: node.frontmatter.slug,
},
});
});
// Create paginated blog index
const posts = result.data.allMarkdownRemark.edges;
const postsPerPage = 6;
const numPages = Math.ceil(posts.length / postsPerPage);
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/blog` : `/blog/${i + 1}`,
component: path.resolve("./src/templates/blog-list.js"),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
});
});
};Source and process data from external systems and files.
/**
* Source nodes from external systems
*/
interface SourceNodesArgs extends ParentSpanPluginArgs {
actions: Actions;
createNodeId: CreateNodeIdFunction;
createContentDigest: CreateContentDigestFunction;
reporter: Reporter;
getCache: GetCacheFunction;
getNode: GetNodeFunction;
getNodes: GetNodesFunction;
hasNodeChanged: HasNodeChangedFunction;
webhookBody?: object;
}
/**
* Process created nodes
*/
interface CreateNodeArgs extends ParentSpanPluginArgs {
node: Node;
actions: Actions;
createNodeId: CreateNodeIdFunction;
createContentDigest: CreateContentDigestFunction;
getNode: GetNodeFunction;
getNodes: GetNodesFunction;
reporter: Reporter;
}
type CreateNodeIdFunction = (input: string) => string;
type CreateContentDigestFunction = (input: string | object) => string;Usage Examples:
// gatsby-node.js - Custom data source
exports.sourceNodes = async ({
actions,
createNodeId,
createContentDigest,
reporter,
}) => {
const { createNode } = actions;
try {
// Fetch data from API
const response = await fetch("https://api.example.com/posts");
const posts = await response.json();
// Create nodes for each post
posts.forEach((post) => {
const nodeId = createNodeId(`Post-${post.id}`);
const nodeContent = JSON.stringify(post);
const node = {
...post,
id: nodeId,
parent: null,
children: [],
internal: {
type: "Post",
content: nodeContent,
contentDigest: createContentDigest(post),
},
};
createNode(node);
});
reporter.info(`Created ${posts.length} Post nodes`);
} catch (error) {
reporter.panicOnBuild("Error sourcing posts from API", error);
}
};
// Transform nodes after creation
exports.onCreateNode = ({ node, actions, createNodeId, createContentDigest }) => {
const { createNode, createParentChildLink } = actions;
// Process Markdown files
if (node.internal.type === "MarkdownRemark") {
// Extract reading time
const readingTime = Math.ceil(node.rawMarkdownBody.split(" ").length / 200);
// Create reading time node
const readingTimeNode = {
id: createNodeId(`ReadingTime-${node.id}`),
parent: node.id,
children: [],
readingTime,
text: `${readingTime} min read`,
internal: {
type: "ReadingTime",
contentDigest: createContentDigest({ readingTime }),
},
};
createNode(readingTimeNode);
createParentChildLink({ parent: node, child: readingTimeNode });
}
};Define and customize the GraphQL schema for data querying.
/**
* Customize GraphQL schema
*/
interface CreateSchemaCustomizationArgs extends ParentSpanPluginArgs {
actions: Actions;
schema: GatsbyGraphQLObjectType;
}
/**
* Add GraphQL resolvers
*/
interface CreateResolversArgs extends ParentSpanPluginArgs {
actions: Actions;
schema: GatsbyGraphQLObjectType;
createResolvers: CreateResolversFunction;
}
type CreateResolversFunction = (resolvers: Record<string, any>) => void;Usage Examples:
// gatsby-node.js - Schema customization
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions;
// Define custom types
const typeDefs = `
type BlogPost implements Node {
id: ID!
title: String!
slug: String!
content: String!
publishedAt: Date! @dateformat
author: Author! @link(by: "id")
tags: [Tag!]! @link(by: "name")
featuredImage: File @fileByRelativePath
readingTime: ReadingTime @link(by: "parent.id")
}
type Author implements Node {
id: ID!
name: String!
email: String!
bio: String
avatar: File @fileByRelativePath
posts: [BlogPost!]! @link(by: "author.id", from: "id")
}
type Tag implements Node {
id: ID!
name: String!
slug: String!
posts: [BlogPost!]! @link(by: "tags.name", from: "name")
}
`;
createTypes(typeDefs);
// Create field extension
const slugify = (str) =>
str
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/(^-|-$)+/g, "");
createTypes(
schema.buildObjectType({
name: "BlogPost",
fields: {
slug: {
type: "String!",
resolve: (source) => slugify(source.title),
},
},
})
);
};
// Add custom resolvers
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
BlogPost: {
relatedPosts: {
type: ["BlogPost!"]!,
resolve: async (source, args, context) => {
const { entries } = await context.nodeModel.findAll({
type: "BlogPost",
query: {
filter: {
tags: { elemMatch: { name: { in: source.tags.map(t => t.name) } } },
id: { ne: source.id },
},
limit: 3,
},
});
return entries;
},
},
},
Author: {
postCount: {
type: "Int!",
resolve: async (source, args, context) => {
const { totalCount } = await context.nodeModel.findAll({
type: "BlogPost",
query: {
filter: { author: { id: { eq: source.id } } },
},
});
return totalCount;
},
},
},
};
createResolvers(resolvers);
};Customize Babel and Webpack configurations for the build process.
/**
* Modify Webpack configuration
*/
interface CreateWebpackConfigArgs extends ParentSpanPluginArgs {
stage: WebpackStage;
rules: WebpackRules;
loaders: WebpackLoaders;
plugins: WebpackPlugins;
actions: Actions;
}
type WebpackStage =
| "develop"
| "develop-html"
| "build-javascript"
| "build-html";
interface WebpackRules {
js: () => object;
eslint: () => object;
yaml: () => object;
fonts: () => object;
images: () => object;
media: () => object;
miscAssets: () => object;
}
interface WebpackLoaders {
json: () => object;
yaml: () => object;
null: () => object;
raw: () => object;
style: () => object;
css: () => object;
postcss: () => object;
file: () => object;
url: () => object;
js: () => object;
dependencies: () => object;
}Usage Examples:
// gatsby-node.js - Webpack customization
exports.onCreateWebpackConfig = ({ stage, loaders, actions, plugins }) => {
const { setWebpackConfig } = actions;
// Add custom webpack configuration
setWebpackConfig({
resolve: {
alias: {
"@components": path.resolve(__dirname, "src/components"),
"@utils": path.resolve(__dirname, "src/utils"),
"@images": path.resolve(__dirname, "src/images"),
},
},
});
// Stage-specific configuration
if (stage === "build-html" || stage === "develop-html") {
setWebpackConfig({
module: {
rules: [
{
test: /canvas/,
use: loaders.null(),
},
],
},
});
}
// Add custom loader
if (stage === "develop" || stage === "build-javascript") {
setWebpackConfig({
module: {
rules: [
{
test: /\.md$/,
use: [
loaders.js(),
{
loader: "frontmatter-markdown-loader",
options: { mode: ["body"] },
},
],
},
],
},
});
}
};
// Babel configuration
exports.onCreateBabelConfig = ({ actions }) => {
actions.setBabelPlugin({
name: "@babel/plugin-proposal-export-default-from",
});
actions.setBabelPreset({
name: "babel-preset-gatsby",
options: {
targets: {
browsers: ["> 1%", "last 2 versions", "IE >= 9"],
},
},
});
};Available actions for manipulating pages, nodes, and build configuration.
/**
* Actions available in gatsby-node.js APIs
*/
interface Actions {
/** Page management */
createPage: (args: CreatePageArgs) => void;
deletePage: (args: DeletePageArgs) => void;
createSlice: (args: CreateSliceArgs) => void;
/** Node management */
createNode: (node: NodeInput) => void;
deleteNode: (node: { id: string }) => void;
touchNode: (node: { id: string }) => void;
createNodeField: (args: CreateNodeFieldArgs) => void;
createParentChildLink: (args: CreateParentChildLinkArgs) => void;
/** Build configuration */
setWebpackConfig: (config: object) => void;
replaceWebpackConfig: (config: object) => void;
setBabelOptions: (options: object) => void;
setBabelPlugin: (args: SetBabelPluginArgs) => void;
setBabelPreset: (args: SetBabelPresetArgs) => void;
/** Schema management */
createTypes: (typeDefs: string | object) => void;
createFieldExtension: (args: CreateFieldExtensionArgs) => void;
addThirdPartySchema: (args: AddThirdPartySchemaArgs) => void;
printTypeDefinitions: (args: PrintTypeDefinitionsArgs) => void;
/** Jobs and processing */
createJob: (job: JobInput, plugin?: IGatsbyPlugin) => void;
createJobV2: (job: JobV2Input, plugin?: IGatsbyPlugin) => void;
setJob: (job: Job, plugin?: IGatsbyPlugin) => void;
endJob: (job: Job, plugin?: IGatsbyPlugin) => void;
/** Other actions */
createRedirect: (redirect: RedirectInput) => void;
setPluginStatus: (status: object, plugin?: IGatsbyPlugin) => void;
setRequestHeaders: (headers: Record<string, string>) => void;
addGatsbyImageSourceUrl: (url: string) => void;
unstable_createNodeManifest: (args: CreateNodeManifestArgs) => void;
addRemoteFileAllowedUrl: (url: string | RegExp) => void;
enableStatefulSourceNodes: () => void;
}Modern deployment adapter system for various hosting platforms.
/**
* Adapter initialization function
*/
type AdapterInit = (options?: object) => IAdapter;
/**
* Adapter interface for deployment platforms
*/
interface IAdapter {
name: string;
cache: Cache;
config: IAdapterConfig;
initializeAdapterStore?: () => Promise<void>;
createRoutes?: (functions?: Array<IFunctionDefinition>) => Promise<RoutesManifest>;
}
interface IAdapterConfig {
deployURL?: string;
excludeDatastoreFromBundle?: boolean;
imageCDN?: boolean;
fileCDN?: boolean;
}
interface IFunctionDefinition {
functionId: string;
originalFilePath: string;
relativeCompiledFilePath: string;
absoluteCompiledFilePath: string;
matches: Array<string>;
}
interface RoutesManifest {
version: 1;
routes: Array<IStaticRoute | IFunctionRoute | IRedirectRoute>;
headers?: HeaderRoutes;
functions?: FunctionsManifest;
}