Skip to Content
Fumadocs

OpenAPI

Generating docs for OpenAPI schema.

Setup

Install the required packages.

npm i fumadocs-openapi shiki

Generate Styles

Add the following line:

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

Configure Plugin

Create the OpenAPI server instance and the <OpenAPIPage /> component.

import { createOpenAPI } from 'fumadocs-openapi/server';

// note: this is a server-side API
export const openapi = createOpenAPI({
  // the OpenAPI schema, you can also give it an external URL.
  input: ['./openapi.json'],
});

See createOpenAPI() and createOpenAPIPage() for their options.

Generate Pages

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

src/lib/source.ts
import { getCollection } from 'astro:content';
import { loader } from 'fumadocs-core/source';
import { toFumadocsSource } from '@fumadocs/astro/source';
import { openapi } from '@/lib/openapi'; 

export const source = loader(
  {
    docs: toFumadocsSource(await getCollection('docs'), await getCollection('meta')),
    openapi: await openapi.staticSource({
      baseDir: 'openapi',
    }),
  },
  {
    baseUrl: '/docs',
    // optional: adds a badge to each page item in page tree
    plugins: [openapi.loaderPlugin()],
  },
);

staticSource() generates a page per operation by default. See Generating Pages to customize them.

Pages of source now have two types, docs and openapi. Update every place that uses source, like where you return text for LLMs:

import { llms } from 'fumadocs-core/source';
import { source } from '@/lib/source';

export const docsLlms = llms(source, {
  renderPage: (page) => {
    if (page.type === 'openapi') {
      // e.g. return the stringified OpenAPI schema
      return JSON.stringify(page.data.getSchema().bundled, null, 2);
    }

    // your original flow below...
  },
});

Render OpenAPI pages in an island of their own, which wraps <OpenAPIPage /> with the docs layout:

---
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro';
import Layout from '@/layouts/Layout.astro';
import { Docs } from '@/components/docs';
import { OpenAPIDocs } from '@/components/openapi-docs'; 
import { source } from '@/lib/source';

export const prerender = true;

export const getStaticPaths = (() => {
  return source.getPages().map((page) => ({
    params: { slug: decodeURI(page.slugs.join('/')) || undefined },
    props: { page },
  }));
}) satisfies GetStaticPaths;

type Props = InferGetStaticPropsType<typeof getStaticPaths>;

const { page } = Astro.props;
const pageTree = await source.serializePageTree(source.getPageTree());
---

{/* [!code ++:13] */}
{page.type === 'openapi' && (
  <Layout title={page.data.title ?? 'My App'}>
    <OpenAPIDocs
      client:load
      pathname={Astro.url.pathname}
      params={Astro.params}
      tree={pageTree}
      title={page.data.title}
      props={page.data.getOpenAPIPageProps()}
    />
  </Layout>
)}

Ensure the migration is complete!

Run a type check before continuing:

npm run types:check

To write MDX files into your content directory instead, see MDX Files.

Features

The official OpenAPI integration supports:

  • Basic API endpoint information
  • Interactive API playground
  • Example code to send request (in different programming languages)
  • Response samples and TypeScript definitions
  • Request parameters and body generated from schemas

Demo

View demo.

How is this guide?

Last updated on