Skip to main content

Hooks Recipes

This page lists examples of usage of hooks.

info

Hooks are part of the documentation frameworks integration API.

Available Hooks

GraphQL-Markdown provides lifecycle hooks for customizing the documentation generation process.

Generation Hooks

HookDescription
beforeSchemaLoadHookCalled before loading the GraphQL schema
afterSchemaLoadHookCalled after loading the GraphQL schema
beforeDiffCheckHookCalled before checking schema differences
afterDiffCheckHookCalled after checking schema differences
beforeRenderRootTypesHookCalled before rendering root types
afterRenderRootTypesHookCalled after rendering root types
beforeRenderHomepageHookCalled before rendering the homepage
afterRenderHomepageHookCalled after rendering the homepage
beforeRenderTypeEntitiesHookCalled before rendering type entities
afterRenderTypeEntitiesHookCalled after rendering type entities
beforeGenerateIndexMetafileHookCalled before generating index metafiles
afterGenerateIndexMetafileHookCalled after generating index metafiles

Printer Hooks

HookDescription
beforePrintCodeHookCalled before generating code blocks — can modify options or prevent default generation
afterPrintCodeHookCalled after generating code blocks — can modify the generated output
beforePrintTypeHookCalled before generating type documentation — can modify options or prevent default
afterPrintTypeHookCalled after generating type documentation — can modify the generated output
beforeComposePageTypeHookCalled before composing type page sections — can reorder, remove, or inject sections

info

All hooks receive an event object with a data property containing context-specific information. Printer hooks also have an output property that can be modified.

For beforeComposePageTypeHook, event.output is the ordered list of section keys to render, and event.data.sections is the section content map.

Using Hooks with Docusaurus

When using Docusaurus, you can extend the default MDX module to add custom hooks while keeping all the built-in formatters:

custom-mdx.cjs
const DocusaurusMDX = require("@graphql-markdown/docusaurus/mdx");

const afterPrintCodeHook = async (event) => {
// Your custom logic here
event.output = `${event.output}\n\n<!-- Custom content -->`;
};

module.exports = {
...DocusaurusMDX, // Keep all default formatters
afterPrintCodeHook, // Add your custom hook
};

Then configure your Docusaurus plugin to use the custom module:

docusaurus.config.js
plugins: [
[
"@graphql-markdown/docusaurus",
{
schema: "./schema.graphql",
mdxParser: require.resolve("./custom-mdx.cjs"),
// ... other options
},
],
],

Generate index.md files

You can use this hook to generate index.md files.

---
title: "Unions"
---

- [ActivityUnion](./activity-union.mdx)
- [LikeableUnion](./likeable-union.mdx)
- [NotificationUnion](./notification-union.mdx)

Declare the custom module in GraphQL-Markdown configuration mdxParser: "./custom-mdx.mjs".

custom-mdx.mjs

import { join, dirname, resolve, basename } from "node:path";
import { appendFile } from "node:fs/promises";

import {
ensureDir,
fileExists,
saveFile,
startCase,
} from "@graphql-markdown/utils";

const INDEX_MD = "index.md";

/**
* Hook that generates an index metadata file for a category directory.
*
* This hook is executed before generating index metadata files.
* It checks if an index.md file exists in the specified directory.
* If not, it creates one with a title derived from the category name.
*/
const beforeGenerateIndexMetafileHook = async (event) => {
const { dirPath, category } = event.data;
const filePath = join(dirPath, INDEX_MD);

if (await fileExists(filePath)) {
return;
}

const label = startCase(category);
const content = `---\ntitle: "${label}"\n---\n\n`;
await ensureDir(dirPath);
await saveFile(filePath, content);
};

/**
* Hook that appends a link to the entity's page in the index file after rendering type entities.
*
* This hook is triggered after a GraphQL type entity is rendered.
* It checks if an index file exists in the same directory as the
* rendered entity, and if so, appends a markdown link to the entity's page.
*/
const afterRenderTypeEntitiesHook = async (event) => {
const { filePath, name } = event.data;
const indexFilePath = resolve(dirname(filePath), INDEX_MD);
const pageFileName = basename(filePath);
if (await fileExists(indexFilePath)) {
const entryLine = `- [${name}](./${pageFileName})\n`;
await appendFile(indexFilePath, entryLine);
}
};

export {
beforeGenerateIndexMetafileHook,
afterRenderTypeEntitiesHook
};

Display response types for operations

You can use this hook to automatically add the response type's SDL (Schema Definition Language) after each GraphQL operation (query, mutation, subscription).

This is useful when you want your documentation to show not just the operation signature but also the structure of the data it returns.

Before (default output):

query user(id: ID!): User

After (with hook):

The hook appends a "Response Type" section after the operation's code block:

query user(id: ID!): User

Response Type

type User {
id: ID!
name: String!
email: String
posts: [Post!]!
}

Declare the custom module in GraphQL-Markdown configuration mdxParser: "./custom-mdx.mjs".

custom-mdx.mjs
import {
isOperation,
isScalarType,
getNamedType
} from "@graphql-markdown/graphql";
import { Printer } from "@graphql-markdown/printer-legacy";

/**
* Hook that adds the response type's SDL after operation code blocks.
*
* This hook is executed after generating the code block for a GraphQL type.
* For operations (queries, mutations, subscriptions), it appends a second
* code block showing the structure of the return type.
*/
const afterPrintCodeHook = async (event) => {
const { type, options } = event.data;

// Only process operations (types with a `type` property for return type)
if (!isOperation(type)) {
return;
}

// Get the unwrapped return type (removes NonNull and List wrappers)
const returnType = getNamedType(type.type);
if (!returnType) {
return;
}

// Skip scalar types - they don't need expanded documentation
if (isScalarType(returnType)) {
return;
}

// Generate the SDL code block for the return type
const returnTypeCode = Printer.printCode(returnType, options);

if (returnTypeCode) {
event.output = `${event.output}\n\n### Response Type\n\n${returnTypeCode}`;
}
};

export { afterPrintCodeHook };
tip

You can customize the heading level or text by changing ### Response Type to match your documentation style.

note

The getNamedType function from @graphql-markdown/graphql unwraps GraphQL type wrappers like NonNull and List. For example, [User!]! becomes User.

Reorder or hide sections before page composition

Use beforeComposePageTypeHook to customize the final section order for each type page. This is the recommended approach for section visibility and ordering.

custom-mdx.mjs
const beforeComposePageTypeHook = async (event) => {
// Hide the generated code section
event.output = event.output.filter((key) => key !== "code");

// Move "relations" before "metadata" when both are present
const relationsIndex = event.output.indexOf("relations");
const metadataIndex = event.output.indexOf("metadata");

if (relationsIndex > -1 && metadataIndex > -1 && relationsIndex > metadataIndex) {
event.output.splice(relationsIndex, 1);
event.output.splice(metadataIndex, 0, "relations");
}
};

export { beforeComposePageTypeHook };