# Fumadocs (Framework Mode, Waku): ヘッドレス
URL: /ja/docs/waku/integrations/graphql/headless
Source: https://raw.githubusercontent.com/fuma-nama/fumadocs/refs/heads/main/apps/docs/content/docs/(framework)/[framework]/integrations/graphql/headless.ja.mdx

UI を含まない、GraphQL ページの状態とロジック。

## 概要 [#overview]

`@fumadocs/graphql/ui` 以外はすべてヘッドレスです。

| パス                             | モジュール                                |
| ------------------------------ | ------------------------------------ |
| `@fumadocs/graphql`            | ページ：SDL から構築したスキーマ、クロスリンク、独自のコンポーネント |
| `@fumadocs/graphql/operation`  | オペレーション：フィールド、タイトル、ディレクティブ、サンプル      |
| `@fumadocs/graphql/type-docs`  | 名前付き型：種別、ディレクティブ、関連、使用箇所             |
| `@fumadocs/graphql/playground` | プレイグラウンドの状態、オペレーションやサブスクリプションの送信     |

## ページ [#page]

`createGraphQLRenderer()` はコンポーネントを受け取り、`<GraphQLPage />` を返します。`<GraphQLPage />` は、生成されたページの props を受け取ります。

```tsx title="components/api-page.tsx"
'use client';
import { type CodeBlockProps, createGraphQLRenderer } from '@fumadocs/graphql';
import { Operation } from '@/components/my-operation';
import { TypeDocs } from '@/components/my-type';

export const GraphQLPage = createGraphQLRenderer({
  components: {
    Operation,
    TypeDocs,
    // `root` はフィールド形式のルート、または `Query.orders()` のようなスキーマ座標です
    SchemaUI: ({ client, root }) => (
      <pre>{`${client.name}: ${typeof root === 'string' ? root : root.type}`}</pre>
    ),
    CodeBlock({ lang, code }: CodeBlockProps) {
      return (
        <pre>
          <code className={`language-${lang}`}>{code}</code>
        </pre>
      );
    },
  },
});
```

| コンポーネント     | レンダリング対象                                     |
| ----------- | -------------------------------------------- |
| `Operation` | ページ内の各オペレーション ([Operation](#operation) を参照)  |
| `TypeDocs`  | ページ内の各名前付き型 ([Type](#type) を参照)              |
| `SchemaUI`  | 型、引数、フィールドの詳細                                |
| `Layout`    | 任意。レンダリングされた項目をラップします                        |

事前生成されたページの[クロスリンク](/docs/waku/integrations/graphql/api-page#cross-linking)を上書きするには、`typeLinks` と `operationLinks` を渡してください。

### フック [#hooks]

ページ配下のコンポーネントは、これらのフックでページの状態を読み取れます。

| フック                            | 戻り値                                            |
| ------------------------------ | ---------------------------------------------- |
| `useGraphQL()`                 | ビルド済みの `schema`、その `sdl`、ページのリンクと例             |
| `useComponents()`              | ページに渡されたコンポーネント                                |
| `useTypeLink(name)`            | 指定した名前の型のページ URL。リンクがない場合は `undefined`         |
| `useOperationLink(kind, name)` | オペレーションのページ URL                                |
| `useRenderContext()`           | `playground` や `codeUsages` などのページのレンダリングオプション |

オペレーションを独自にレンダリングするには、`createGraphQLRenderer()` の代わりに `<GraphQLProvider sdl={sdl} components={...} />` をマウントしてください。

## オペレーション [#operation]

`<OperationProvider />` は、オペレーションの詳細情報を導き出します。

```tsx title="components/my-operation.tsx"
'use client';
import { useComponents } from '@fumadocs/graphql';
import {
  OperationProvider,
  type PageOperationProps,
  useOperation,
} from '@fumadocs/graphql/operation';

export function Operation({ kind, name }: PageOperationProps) {
  return (
    <OperationProvider kind={kind} name={name}>
      <Content />
    </OperationProvider>
  );
}

function Content() {
  const { title, field, examples, selected, select } = useOperation();
  const { CodeBlock, SchemaUI } = useComponents();
  const example = examples.at(selected);

  return (
    <>
      <h2>{title}</h2>
      {field.args.map((arg) => (
        <SchemaUI
          key={arg.name}
          client={{ name: arg.name, rootId: arg.name }}
          root={{ type: arg.type }}
        />
      ))}
      {examples.map((item, i) => (
        <button key={i} onClick={() => select(i)}>
          {item.name ?? 'Example'}
        </button>
      ))}
      {example && <CodeBlock lang="graphql" code={example.query} />}
    </>
  );
}
```

`examples` は、[`examples` オプション](/docs/waku/integrations/graphql/server#examples)で指定したもの、または自動生成されたものです。`selected` は、サンプルカードやプレイグラウンドなど、プロバイダー配下のすべての要素で共有されます。

### OperationInfo

| Prop         | Type                                  | Description                                                                                                           |
| ------------ | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `kind`       | `GraphQL.OperationKind`               |                                                                                                                       |
| `name`       | `string`                              |                                                                                                                       |
| `title`      | `string`                              | display title of the operation, e.g. `createOrder` -> `Create Order`.                                                 |
| `field`      | `GraphQLField<unknown, unknown, any>` |                                                                                                                       |
| `directives` | `DirectiveNode[]`                     | custom directive applications                                                                                         |
| `examples`   | `GraphQL.OperationExample[]`          | examples from the `examples` option of server, otherwise a generated one, empty when the operation cannot be sampled. |
| `selected`   | `number`                              | index of the selected example, shared by the example cards and playground                                             |
| `select`     | `(index: number) => void`             |                                                                                                                       |

### OperationExample

| Prop           | Type                                   | Description                                             |
| -------------- | -------------------------------------- | ------------------------------------------------------- |
| `name?`        | `string \| undefined`                  | the title of example, `undefined` for the generated one |
| `description?` | `string \| undefined`                  | Markdown                                                |
| `query`        | `string`                               |                                                         |
| `variables?`   | `Record<string, unknown> \| undefined` |                                                         |
| `response`     | `unknown`                              |                                                         |

## 型 [#type]

`<TypeProvider />` は、名前付きの型の詳細情報を導出します。

```tsx title="components/my-type.tsx"
'use client';
import { useComponents } from '@fumadocs/graphql';
import { type PageTypeProps, TypeProvider, useNamedType } from '@fumadocs/graphql/type-docs';

export function TypeDocs({ name }: PageTypeProps) {
  return (
    <TypeProvider name={name}>
      <Content />
    </TypeProvider>
  );
}

function Content() {
  const { name, kind, type, relations } = useNamedType();
  const { SchemaUI } = useComponents();

  return (
    <>
      <h2>
        {name} ({kind})
      </h2>
      {relations.usages.returnedBy.map((op) => (
        <span key={`${op.kind}:${op.name}`}>{op.name}</span>
      ))}
      <SchemaUI client={{ name, rootId: name, as: 'body' }} root={{ type }} />
    </>
  );
}
```

### NamedTypeInfo

| Prop         | Type                                                                                                                                                                                                           | Description                   |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `name`       | `string`                                                                                                                                                                                                       |                               |
| `kind`       | `GraphQL.NamedTypeKind`                                                                                                                                                                                        |                               |
| `type`       | `GraphQLNamedType`                                                                                                                                                                                             |                               |
| `directives` | `DirectiveNode[]`                                                                                                                                                                                              | custom directive applications |
| `relations`  | `{ implements: readonly GraphQLInterfaceType[]; unions: readonly GraphQLUnionType[]; implementedBy: readonly GraphQLObjectType[]; possibleTypes: readonly GraphQLObjectType[]; usages: TypeDocs.TypeUsages; }` |                               |

`relations` は、その型とスキーマ内の他の要素との関係を表します。

| リレーション          | 内容                            |
| --------------- | ----------------------------- |
| `implements`    | その型が実装するインターフェース              |
| `unions`        | その型を含むユニオン (オブジェクトの場合)        |
| `implementedBy` | その型を実装するオブジェクト (インターフェースの場合)  |
| `possibleTypes` | ユニオンのメンバー                     |
| `usages`        | スキーマ内でその型が使用されている箇所           |

### TypeUsages

| Prop         | Type                      | Description                                                                                  |
| ------------ | ------------------------- | -------------------------------------------------------------------------------------------- |
| `returnedBy` | `TypeDocs.OperationRef[]` | operations whose return type resolves to this named type (list/non-null wrappers unwrapped). |
| `memberOf`   | `TypeDocs.FieldRef[]`     | object/interface/input object fields typed as this named type.                               |
| `inputFor`   | `TypeDocs.OperationRef[]` | operations with a direct argument of this named type.                                        |
| `argumentOf` | `TypeDocs.FieldRef[]`     | field arguments (on non-root types) typed as this named type.                                |

## プレイグラウンド [#playground]

`usePlayground()` は、`<OperationProvider />` 配下にあるオペレーションのプレイグラウンドを保持します。プレイグラウンドは、ページの [`playground` オプション](/docs/waku/integrations/graphql/api-page#playground)で設定します。

```tsx title="components/my-playground.tsx"
'use client';
import { StfProvider, useFieldValue } from '@fumari/stf';
import { useOperation } from '@fumadocs/graphql/operation';
import { usePlayground } from '@fumadocs/graphql/playground';

export function MyPlayground() {
  const { field } = useOperation();
  const { stf, open, url, changeUrl, query, setQuery, send, isSending, response } = usePlayground();

  return (
    // 開くと、前回アクセス時のエンドポイントとヘッダーが復元されます
    <details onToggle={(e) => e.currentTarget.open && open()}>
      <summary>Try it</summary>
      <input value={url} onChange={(e) => changeUrl(e.target.value)} />
      <textarea value={query} onChange={(e) => setQuery(e.target.value)} />
      <StfProvider value={stf}>
        {field.args.map((arg) => (
          <Variable key={arg.name} name={arg.name} />
        ))}
      </StfProvider>
      <button disabled={isSending} onClick={send}>
        Send
      </button>
      {response?.result.type === 'response' && <pre>{response.result.body}</pre>}
    </details>
  );
}

function Variable({ name }: { name: string }) {
  const [value, setValue] = useFieldValue(['variables', name]);

  return (
    <input
      aria-label={name}
      value={String(value ?? '')}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}
```

サブスクリプションでは、`send()` と `response` の代わりに `subscribe()`、`stop()`、`status`、`log` を使用します。

### Playground

| Prop            | Type                                               | Description                                                                                                       |
| --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `stf`           | `Stf`                                              | the form: `variables` and `headers` of the request                                                                |
| `url`           | `string`                                           | the endpoint, empty until `open()` restores it                                                                    |
| `changeUrl`     | `(url: string) => void`                            | headers are kept by endpoint origin, a new origin swaps them out                                                  |
| `query`         | `string`                                           |                                                                                                                   |
| `setQuery`      | `(query: string) => void`                          |                                                                                                                   |
| `loaded`        | `number`                                           | index of the example in the editor and form                                                                       |
| `example?`      | `GraphQL.OperationExample \| undefined`            |                                                                                                                   |
| `select`        | `(index: number) => void`                          | select an example on the page, and load it                                                                        |
| `reset`         | `() => void`                                       | load the selected example again                                                                                   |
| `open`          | `() => void`                                       | when the dialog opens: restore the endpoint and headers of previous visits, load the example selected on the page |
| `send`          | `() => Promise<void>`                              | send a query or mutation, its result becomes `response`                                                           |
| `isSending`     | `boolean`                                          |                                                                                                                   |
| `response?`     | `PlaygroundModule.PlaygroundResponse \| undefined` |                                                                                                                   |
| `clearResponse` | `() => void`                                       |                                                                                                                   |
| `subscribe`     | `() => void`                                       | start a subscription, its events are added to `log`                                                               |
| `stop`          | `() => void`                                       |                                                                                                                   |
| `status`        | `PlaygroundModule.ConnectionStatus`                |                                                                                                                   |
| `log`           | `PlaygroundModule.LogEntry[]`                      |                                                                                                                   |
| `clearLog`      | `() => void`                                       |                                                                                                                   |

| エクスポート                                | 役割                                                                           |
| ------------------------------------- | ---------------------------------------------------------------------------- |
| `useRequestValues()`                  | `<StfProvider />` 配下のフォームが送信する `variables` と `headers` を返します。フォームの編集内容に追従します |
| `executeGraphQL(request)`             | クエリまたはミューテーションを HTTP POST で送信します。デフォルトの `fetcher` です                         |
| `subscribeGraphQL(request, handlers)` | サブスクリプションを開始し、停止用の関数を返します                                                    |
| `inputTypeToJsonSchema(type)`         | 入力型の JSON Schema を返します。変数のフォームに使用します                                         |

`subscribeGraphQL()` は、[`subscriptionUrl`](/docs/waku/integrations/graphql/api-page#subscriptions) と同様に、URL スキームに応じてプロトコルを選択します。

```ts
import { subscribeGraphQL } from '@fumadocs/graphql/playground';

const stop = subscribeGraphQL(
  {
    url: 'wss://api.example.com/graphql',
    query: 'subscription { orderUpdated(id: "1") { status } }',
    headers: {},
  },
  {
    onNext: (result) => console.log(result),
    onError: (error) => console.error(error.type),
    onComplete: () => console.log('completed'),
  },
);

// 呼び出し後はどのハンドラーも呼ばれません
stop();
```

### SubscriptionHandlers

| Prop         | Type                                                  | Description                                                                                                               |
| ------------ | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `onOpen?`    | `(() => void) \| undefined`                           | the subscription started                                                                                                  |
| `onNext`     | `(result: unknown) => void`                           | an execution result of the subscription, like `{ data }` or `{ errors }`, including the errors of a rejected subscription |
| `onError`    | `(error: PlaygroundModule.SubscriptionError) => void` | the connection failed, it ends the subscription                                                                           |
| `onComplete` | `() => void`                                          | the server completed the subscription, it ends the subscription                                                           |