Fumadocs

createAsyncAPI()

The AsyncAPI server instance.

AsyncAPI Server

The main config for Fumadocs AsyncAPI.

It should not be referenced in browser environments.

input

The AsyncAPI schemas to read from.

  • File Paths
  • External URLs
  • Functions (see below)

Only schemas listed here can be loaded, other IDs passed to getSchema() or preloaded by pages throw an error.

Do not pass untrusted schemas to input, their $refs can read local files and fetch URLs, private hosts included.

import { createAsyncAPI } from '@fumadocs/asyncapi/server';

export const asyncapi = createAsyncAPI({
  input: ['./streetlights.yaml'],
});

disableCache

Disable caching of loaded schemas. Useful during development when schemas change frequently.

import { createAsyncAPI } from '@fumadocs/asyncapi/server';

export const asyncapi = createAsyncAPI({
  input: ['./asyncapi.yaml'],
  disableCache: true,
});

Loading Documents Once

Pages receive the whole document in their props, every page load, navigation and prefetch sends it again. For large documents, leave it out:

lib/asyncapi.ts
import { createAsyncAPI } from '@fumadocs/asyncapi/server';

export const asyncapi = createAsyncAPI({
  // IDs end up in URLs, keep them URL-safe
  input: { streetlights: './streetlights.yaml' },
  includeDocument: false, 
});

On Next.js, Waku and React Router, <AsyncAPIPage /> loads each document once per browser session with loadDocument, it also runs during server rendering:

components/api-page.tsx
'use client';
import { preload } from 'react-dom';
import { createAsyncAPIPage } from '@fumadocs/asyncapi/ui';

export const AsyncAPIPage = createAsyncAPIPage({
  async loadDocument(id) {
    // `import.meta.env.SSR` on Waku and React Router
    if (typeof window === 'undefined') {
      // the browser starts downloading it with the page
      preload(`/api/asyncapi/${id}`, { as: 'fetch', crossOrigin: 'anonymous' });
      const { asyncapi } = await import('@/lib/asyncapi');
      return (await asyncapi.getSchema(id)).bundled;
    }
    return (await fetch(`/api/asyncapi/${id}`)).json();
  },
});

And serve documents to the browser:

app/api/asyncapi/[id]/route.ts
import { asyncapi } from '@/lib/asyncapi';

export async function GET(_req: Request, { params }: RouteContext<'/api/asyncapi/[id]'>) {
  return Response.json((await asyncapi.getSchema((await params).id)).bundled);
}

// for static exports
export async function generateStaticParams() {
  return Object.keys(await asyncapi.getSchemas()).map((id) => ({ id }));
}

On Tanstack Start, route loaders run on both sides, call a server function only when the browser doesn't have the document yet:

src/routes/docs/$.tsx
import { createServerFn } from '@tanstack/react-start';
import type { AsyncAPI } from '@fumadocs/asyncapi';
import { asyncapi } from '@/lib/asyncapi';

// documents the browser has loaded
const documents = new Map<string, AsyncAPI.AsyncAPIObject>();

const getDocument = createServerFn({ method: 'GET' })
  .validator((id: string) => id)
  .handler(async ({ data: id }) => (await asyncapi.getSchema(id)).bundled);

export const Route = createFileRoute('/docs/$')({
  component: Page,
  loader: async ({ params }) => {
    const data = await serverLoader({ data: params._splat?.split('/') ?? [] });
    if (data.type === 'asyncapi') {
      const { document: id, payload } = data.props;
      payload.bundled = documents.get(id) ?? (await getDocument({ data: id }));
    }
    return data;
  },
});

function Page() {
  const page = useFumadocsLoader(Route.useLoaderData());
  // the document of the first page too
  if (page.type === 'asyncapi' && typeof window !== 'undefined')
    documents.set(page.props.document, page.props.payload.bundled!);
  // ...
}
  • With generated MDX files, only Tanstack Start needs a change: put documents in preloaded.docs instead.
  • For the UI installed by Fumadocs CLI, pass loadDocument to createAsyncAPIRenderer().
  • Browsers keep each document until a reload. With disableCache, leave includeDocument on.

How is this guide?

Last updated on