# Fumadocs (Framework Mode, Astro): createOpenAPIPage()
URL: /zh-TW/docs/astro/integrations/openapi/api-page
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/(framework)/[framework]/integrations/openapi/api-page.zh-TW.mdx

用於算繪 OpenAPI 文件內容的元件

## 概覽 [#overview]

Fumadocs OpenAPI 使用 `<OpenAPIPage />` 元件來算繪頁面內容，此元件應為用戶端元件。

```tsx title="components/api-page.tsx"
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';

export const OpenAPIPage = createOpenAPIPage({
  // 設定
});
```

### 產生程式碼使用範例 [#generate-code-usages]

為每個 API 端點產生程式碼使用範例。

```tsx title="components/api-page.tsx" twoslash
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import {
  createCodeUsageGeneratorRegistry,
  type CodeUsageGenerator,
} from 'fumadocs-openapi/requests/generators';
import { registerDefault } from 'fumadocs-openapi/requests/generators/all';

const codeUsages = createCodeUsageGeneratorRegistry();

// 加入預設產生器
registerDefault(codeUsages);

// 新增自訂產生器
codeUsages.add('custom-id', {
  label: 'My Example',
  lang: 'js',
  generate(data, { mediaAdapters }) {
    // 請求資料
    console.log(data);

    return 'const response = "hello world";';
  },
});

export const OpenAPIPage = createOpenAPIPage({
  // [!code ++]
  codeUsages,
});
```

此外，您也可以透過 OpenAPI schema 指定程式碼範例。

```yaml
paths:
  /plants:
    get:
      x-codeSamples:
        - lang: js
          label: JavaScript SDK
          source: |
            const planter = require('planter');
            planter.list({ unwatered: true });
```

### 國際化 [#internationalization]

假設您已在 UI 層級設定好[國際化](/docs/astro/internationalization)：

```tsx title="layout.shared.tsx"
import { defineI18n } from 'fumadocs-core/i18n';
import { uiTranslations } from 'fumadocs-ui/i18n';
import { openapiTranslations } from 'fumadocs-openapi/i18n';

const i18n = defineI18n({
  languages: ['en', 'cn'],
  defaultLanguage: 'en',
});

export const translations = i18n
  .translations()
  .extend(uiTranslations())
  // [!code ++]
  .extend(openapiTranslations())
  .add({
    cn: {
      displayName: 'Chinese',
      // [!code ++]
      'Body(playground)': '請求正文',
    },
  });
```

詳情請參閱[翻譯](/docs/ui/translations)。

## 媒體轉接器 [#media-adapters]

您可以建立媒體轉接器，讓 API 頁面支援其他媒體類型。媒體轉接器需實作以下功能：

* 將值轉換為對應媒體類型適用的 `fetch()` body。
* 針對不同的程式語言／工具產生程式碼範例。

其他媒體類型的請求 body 為唯讀：不會出現在程式碼使用範例中，也無法在互動測試區中送出。

```tsx title="components/api-page.tsx" twoslash
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import type { MediaAdapter } from 'fumadocs-openapi';

const mediaAdapters: Record<string, MediaAdapter> = {
  // 範例：自訂 `application/json
  'application/json': {
    encode(data) {
      return JSON.stringify(data.body);
    },
    // 回傳用來初始化 `body` 變數的程式碼，作為請求 body 使用
    generateExample(data, ctx) {
      if (ctx.lang === 'js') {
        return `const body = "hello world"`;
      }

      if (ctx.lang === 'python') {
        return `body = "hello world"`;
      }

      if (ctx.lang === 'go' && 'addImport' in ctx) {
        ctx.addImport('strings');

        return `body := strings.NewReader("hello world")`;
      }
    },
  },
};

export const OpenAPIPage = createOpenAPIPage({
  // [!code ++]
  mediaAdapters,
});
```

## 授權 [#authorization]

驗證提供者 (auth provider) 負責處理互動測試區的安全性方案，包括其值的輸入列，以及這些值的傳送方式。

```tsx title="components/api-page.tsx"
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import { Custom } from 'fumadocs-openapi/ui/playground/client';
import type { AuthPanelProps } from 'fumadocs-openapi/playground';

export const OpenAPIPage = createOpenAPIPage({
  playground: {
    authProviders: [
      {
        // 可為 scheme ID 或函式；省略時套用至所有 scheme
        on: 'my-auth',
        render: ({ Value, Panel }) => (
          <>
            <Value name="Authorization" type="header" start="Bearer" />
            <Panel title="Sign in" component={SignIn} />
          </>
        ),
      },
    ],
  },
});

function SignIn({ field, close }: AuthPanelProps) {
  const { setValue } = Custom.useController(field.fieldName);

  return (
    <button
      onClick={async () => {
        const res = await fetch('/api/token');
        setValue(await res.text());
        close();
      }}
    >
      Sign in
    </button>
  );
}
```

| 選項          | 說明                                        |
| ----------- | ----------------------------------------- |
| `on`        | 它所處理的安全性方案                                |
| `render`    | 它的各列，由 `Value` (值的輸入欄位) 與 `Panel` (面板) 組成 |
| `onRequest` | 將值寫入請求資料，供請求與程式碼使用範例使用 (會遮蔽顯示)            |

請在元件外部定義 provider，因為 provider 一旦變更，驗證欄位就會重新建立。

各列與面板各自保有自己的狀態，可透過 `Custom.useController(field.fieldName)` 讀寫該值。在頁籤開啟期間，值會依方案分別保存。權杖會連同其方案一併送出，例如 `Bearer <token>`。

<Accordions>
<Accordion title="需要離開頁面的登入流程">
請使用 `useAuthRedirect()` 回傳的函式離開頁面。頁面重新載入後，互動測試區會重新開啟，各列則會在 `onReturn` 中取回資料。

```tsx
import { Custom } from 'fumadocs-openapi/ui/playground/client';
import {
  type AuthPanelProps,
  type AuthRenderProps,
  useAuthRedirect,
} from 'fumadocs-openapi/playground';

function Rows({ field, Value, Panel }: AuthRenderProps) {
  const { setValue } = Custom.useController(field.fieldName);

  useAuthRedirect(field, (url, state) => {
    if (url.searchParams.get('state') === state)
      setValue(url.searchParams.get('token')!);
  });

  return (
    <>
      <Value name="Authorization" type="header" start="Bearer" />
      <Panel title="Sign in" component={SignIn} />
    </>
  );
}

function SignIn({ field }: AuthPanelProps) {
  const redirect = useAuthRedirect(field);

  return (
    <button
      onClick={() => {
        const state = crypto.randomUUID();
        redirect(`https://auth.example.com/login?state=${state}`, state);
      }}
    >
      Sign in
    </button>
  );
}
```
</Accordion>
</Accordions>

## 自訂 UI [#customise-ui]

若現有選項無法滿足您的自訂需求，您可以使用 [Fumadocs CLI](/docs/cli) 將 UI 安裝到自己的程式碼庫中。

### 完整 UI [#full-ui]

API 頁面的完整 UI，建構於[無頭式層](/docs/astro/integrations/openapi/headless)之上。

```npm
npx @fumadocs/cli add openapi/page
```

它會直接安裝 `<OpenAPIPage />`，因此不再需要 `components/api-page.tsx`。請更新 `<OpenAPIPage />` 的匯入路徑：

```tsx
import { OpenAPIPage } from '@/components/api-page'; // [!code --]
import { OpenAPIPage } from '@/components/openapi/page'; // [!code ++]
```

### API 互動測試區 [#api-playground]

互動測試區的 UI，其狀態由 [`usePlayground()`](/docs/astro/integrations/openapi/headless#playground) 提供。

```npm
npx @fumadocs/cli add openapi/playground
```

```tsx title="components/api-page.tsx"
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import PlaygroundClient from '@/components/openapi/playground';

export const OpenAPIPage = createOpenAPIPage({
  playground: {
    render: () => <PlaygroundClient />,
  },
});
```

### Operation [#operation]

operation 與 webhook 的 UI。

```npm
npx @fumadocs/cli add openapi/operation
```

```tsx title="components/api-page.tsx"
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import { Operation } from '@/components/openapi/operation';

export const OpenAPIPage = createOpenAPIPage({
  components: {
    Operation,
  },
});
```

### 結構描述 UI [#schema-ui]

用於呈現 JSON schema 的 UI，其狀態來自 [`<SchemaUIProvider />`](/docs/astro/integrations/openapi/headless#schema-ui)。

```npm
npx @fumadocs/cli add api-docs/schema
```

```tsx title="components/api-page.tsx"
'use client';
import { createOpenAPIPage } from 'fumadocs-openapi/ui';
import { Schema } from '@/components/api/schema';

export const OpenAPIPage = createOpenAPIPage({
  components: {
    SchemaUI: Schema,
  },
});
```