コンテンツへスキップ
Fumadocs

createOpenAPI()

OpenAPI のサーバーインスタンスです。

OpenAPI Server

Fumadocs OpenAPI のメイン設定です。

ブラウザ環境から参照しないでください。

input

読み込む OpenAPI スキーマです。

  • ファイルパス
  • 外部 URL
  • 関数 (後述)

読み込めるのはここに指定したスキーマのみです。それ以外の ID を getSchema() に渡したり、ページでプリロードしたりするとエラーがスローされます。

信頼できないスキーマを input に渡さないでください。スキーマ内の $ref によって、ローカルファイルが読み取られたり、プライベートホストを含む任意の URL がフェッチされたりする可能性があります。

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

export const openapi = createOpenAPI({
  input: ['./unkey.json'],
});

ドキュメントを一度だけ読み込む

ページは props でドキュメント全体を受け取るため、ページの読み込み、ナビゲーション、プリフェッチのたびにドキュメントが再送信されます。ドキュメントが大きい場合は、props に含めないようにしてください。

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

export const openapi = createOpenAPI({
  // ID は URL に含まれるため、URL セーフにしてください
  input: { museum: './museum.yaml' },
  includeDocument: false, 
});

TanStack Start のルートローダーはサーバーとブラウザの両方で実行されるため、docs ルートでは loadDocument を使わずにドキュメントを読み込めます。server function は、ブラウザにまだドキュメントがない場合にのみ呼び出してください。

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

// ブラウザで読み込み済みのドキュメント
const documents = new Map<string, Document>();

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

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

function Page() {
  const page = useFumadocsLoader(Route.useLoaderData());
  // 最初のページのドキュメントも保持する
  if (page.type === 'openapi' && typeof window !== 'undefined')
    documents.set(page.props.document, page.props.payload.bundled!);
  // ...
}
  • 生成された MDX ファイルを使用する場合は、代わりにドキュメントを preloaded.docs に配置してください。
  • ブラウザは各ドキュメントをリロードまで保持します。disableCache を使用する場合は、includeDocument を有効にしたままにしてください。

プロキシの作成

プロキシサーバーは、ブラウザの CORS 制限を受けずに HTTP リクエストを送信します。API プレイグラウンドでは、次の用途でプロキシを使用します。

  • CORS で自分のオリジンが許可されていない API エンドポイントへのリクエスト。
  • コンフィデンシャルクライアント (クライアントシークレットを使用) の OAuth リクエストおよびデバイスフロー。

プロキシは認証なしで公開されます。Authorization を含め、リクエストのヘッダーとボディはそのまま転送されます。allowedOrigins と filterRequest でアクセスを制限してください。

セットアップ

プロキシ用のルートハンドラーを作成します。

src/routes/api/proxy.ts
import { openapi } from '@/lib/openapi';
import { createFileRoute } from '@tanstack/react-router';

const proxy = openapi.createProxy();

export const Route = createFileRoute('/api/proxy')({
  server: {
    handlers: {
      // デフォルトではすべてのメソッドを処理します(GET、POST など個別のメソッドを指定することも可能)
      ANY: async ({ request }) => {
        return proxy.handle(request);
      },
    },
  },
});

次に、createOpenAPI() でこのルートの URL を設定します。

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

export const openapi = createOpenAPI({
  proxyUrl: '/api/proxy', 
});

許可するオリジン

デフォルトでは、プロキシはあらゆるパブリックなオリジンを受け付けますが、プライベートネットワーク上のホスト (例: localhost) は拒否し、サイトの Cookie も転送しません。 プロキシの対象を制限したい場合や、プライベートな API をプロキシしたい場合は、プロキシのルートで許可するオリジンを指定してください。

openapi.createProxy({
  // 文字列または正規表現
  allowedOrigins: ['https://api.example.com', 'http://localhost:8080'],
});

リダイレクト先も同じルールでチェックされます。

プライベートネットワークの拒否には Node.js または Deno が必要です。Bun などの他のランタイムでは、 allowedOrigins を指定してください。

リクエストを拒否するには filterRequest を使用します。たとえば、読み取りのみを許可するには次のようにします。

openapi.createProxy({
  // false を返したリクエストはプロキシされずに拒否されます
  filterRequest: (request) => request.method === 'GET',
});

OAuth リダイレクト

OAuth プロバイダーは登録済みのリダイレクト URI しか受け付けませんが、API プレイグラウンドはデフォルトで現在のページにリダイレクトします。 そこで、全ページ共通のリダイレクト URI となるルートハンドラーを作成してください。このハンドラーは、フローを開始したページへユーザーを戻します。

src/routes/api/oauth.ts
import { openapi } from '@/lib/openapi';
import { createFileRoute } from '@tanstack/react-router';

const handler = openapi.createOAuthHandler();

export const Route = createFileRoute('/api/oauth')({
  server: {
    handlers: {
      GET: async ({ request }) => handler(request),
    },
  },
});

このルートの URL を createOpenAPIPage() に渡し、完全な URL(例: https://docs.example.com/api/oauth)を OAuth プロバイダーに登録してください。

components/api-page.tsx
export const OpenAPIPage = createOpenAPIPage({
  oauthRedirectUrl: '/api/oauth', 
});

Content Security Policy

ハンドラーは、自身のルートから配信されるスクリプトを使ってユーザーを元のページに戻します。このルートに Content Security Policy が適用される場合は、'self' のスクリプトを許可する必要があります。'strict-dynamic' を含むポリシーでは 'self' が無視されるため、代わりにこのルートをポリシーの対象から除外してください。

静的サイト

静的サイトでは、ルートはファイルとしてプリレンダリングされます。inline: true を渡してスクリプトをページ内に埋め込み、静的ホストがファイルを HTML として配信できるよう、パスの末尾を .html にしてください。

import { openapi } from '@/lib/openapi';
import { createFileRoute } from '@tanstack/react-router';

const handler = openapi.createOAuthHandler({ inline: true });

export const Route = createFileRoute('/api/oauth.html')({
  server: {
    handlers: {
      GET: async ({ request }) => handler(request),
    },
  },
});

oauthRedirectUrl: '/api/oauth.html' のように、.html の URL をリダイレクト URI として指定してください。スクリプトがページ内に埋め込まれるため、このルートに適用される Content Security Policy でインラインスクリプトを許可する必要があります。

How is this guide?

最終更新日