Skip to Content
Fumadocs

GraphQL

Generating docs for GraphQL schema.

Setup

Install the required packages.

npm i @fumadocs/graphql graphql

Generate Styles

Add the following line:

Tailwind CSS
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
@import '@fumadocs/graphql/css/preset.css';

Configure Plugin

Create the GraphQL server instance and the <GraphQLPage /> component.

import { createGraphQL } from '@fumadocs/graphql/server';

// note: this is a server-side API
export const graphql = createGraphQL({
  // the GraphQL schema, it accepts:
  // SDL files/URLs (including `extend type`), SDL text,
  // introspection results, and `GraphQLSchema` instances.
  input: ['./schema.graphql'],
});

See createGraphQL() and createGraphQLPage() for their options.

Generate Pages

Pass staticSource() to your loader(). It generates pages without writing files.

staticSource() reads files, so source.ts becomes server-only. Move docs into collections.ts for client code to import.

import { loader } from 'fumadocs-core/source';
import { defineDocs } from 'fumadocs-mdx/macro'; 
import { docs } from '@/lib/collections';
import { graphql } from '@/lib/graphql';

export const docs = defineDocs({
  dir: 'content/docs',
  docs: {
    async: true,
  },
});

// server-only now, as `staticSource()` reads files
export const source = loader(
  {
    docs: docs.toFumadocsSource(),
    graphql: await graphql.staticSource({
      baseDir: 'graphql',
    }),
  },
  {
    baseUrl: '/docs',
    // optional: adds a query/mutation/subscription badge to each page item in page tree
    plugins: [graphql.loaderPlugin()],
  },
);

staticSource() generates a page per operation, grouped into folders by kind (e.g. queries/). Pages of named types are opt-in.

Pages of source now have two types, docs and graphql. Update every place that uses source.

Render GraphQL pages with <GraphQLPage /> in your docs page:

src/routes/docs/$.tsx
import { docs, source } from '@/lib/source'; 
import { docs } from '@/lib/collections';
import { source } from '@/lib/source';
import { GraphQLPage } from '@/components/api-page';
import type { OperationExample } from '@fumadocs/graphql';

// the type-level check of server function results can't verify the examples of operations,
// register them as serializable (install `@tanstack/router-core` if it isn't resolvable)
declare module '@tanstack/router-core' {
  interface SerializableExtensions {
    graphqlExample: OperationExample;
  }
}

export const Route = createFileRoute('/docs/$')({
  component: Page,
  loader: async ({ params }) => {
    const slugs = params._splat?.split('/') ?? [];
    const data = await serverLoader({ data: slugs });
    if (data.type === 'docs') await docs.getPage(data.path)?.preload(); 
    return data;
  },
});

const serverLoader = createServerFn({
  method: 'GET',
})
  .validator((slugs: string[]) => slugs)
  .handler(async ({ data: slugs }) => {
    const page = source.getPage(slugs);
    if (!page) throw notFound();

    const pageTree = await source.serializePageTree(source.getPageTree());
    if (page.type === 'graphql') {
      return {
        type: 'graphql',
        title: page.data.title,
        props: page.data.getGraphQLPageProps(),
        pageTree,
      };
    }

    return { type: 'docs', path: page.path, pageTree }; 
  });

function Page() {
  const data = useFumadocsLoader(Route.useLoaderData());

  return (
    <DocsLayout {...baseOptions()} tree={data.pageTree}>
      {data.type === 'graphql' && (
        <DocsPage full>
          <DocsTitle>{data.title}</DocsTitle>
          <DocsBody>
            <GraphQLPage {...data.props} />
          </DocsBody>
        </DocsPage>
      )}
      <Suspense>
        {data.type === 'docs' && <Content path={data.path} />}
      </Suspense>
    </DocsLayout>
  );
}

Ensure the migration is complete!

Run a type check before continuing:

npm run types:check

Features

The official GraphQL integration supports:

  • A page per operation (query/mutation/subscription), with the types it uses explored in place
  • Arguments, fields, deprecations, default values and @oneOf input objects
  • Directives applied in the schema, with their definitions
  • Example requests and responses, generated or from your own operations
  • Code samples in cURL, JavaScript, Go, Python, Java, C# and Rust
  • A playground with a variables form, it runs subscriptions over WebSocket or SSE too
  • Markdown of pages for LLMs
  • Optional pages for named types, with their usages across the schema

Demo

See the GraphQL example.

How is this guide?

Last updated on