Guide/guides

OpenAPI

ルートの定義から、OpenAPI 3.1 のドキュメントを生成できます。スキーマは各ルートのコントラクトから取り、ドキュメントにだけ使う注釈をいくつか書き足せます。オプションの @guren/openapi パッケージを入れると、ドキュメントをファイルに書き出せるほか、対話式のドキュメント UI と合わせてアプリから配信することもできます。

OpenAPI

ルートの定義から、OpenAPI 3.1 のドキュメントを生成できます。スキーマは各ルートのコントラクトから取り、ドキュメントにだけ使う注釈をいくつか書き足せます。オプションの @guren/openapi パッケージを入れると、ドキュメントをファイルに書き出せるほか、対話式のドキュメント UI と合わせてアプリから配信することもできます。

ドキュメントの生成

オプションの @guren/openapi パッケージをインストールすると、ルート定義からスペックを生成できます。

bun add @guren/openapi
bunx guren openapi:generate

このコマンドはルートファイルを読み取り、ルートコントラクトから Zod スキーマと OpenAPI メタデータを取り出して、OpenAPI 3.1 の JSON ドキュメントを .guren/openapi.gen.json に書き出します。

OpenAPI メタデータ

ルートコントラクトには、簡単な OpenAPI のアノテーションも指定できます。指定した内容はルート定義に保存され、オプションの @guren/openapi プラグインが OpenAPI 3.1 のドキュメントを生成するときに使われます。

router.post('/posts', {
  body: CreatePostSchema,
  output: PostResponseSchema,
  name: 'posts.store',
  summary: 'Create a post',
  description: 'Creates a new blog post.',
  tags: ['Posts'],
}, [PostsController, 'store'])

router.get('/posts/:id', {
  params: PostIdParams,
  name: 'posts.show',
  summary: 'Get a post',
  tags: ['Posts'],
  deprecated: false,
}, [PostsController, 'show'])

使える OpenAPI のフィールドは次のとおりです。

フィールド 型 用途
summary string ドキュメント UI に表示される短い説明
description string エンドポイントの詳細な説明
tags string[] ドキュメント UI でエンドポイントをグループ化
operationId string 自動生成されるオペレーション ID を上書き
deprecated boolean エンドポイントを非推奨としてマーク

CLI オプション

# タイトルとバージョンを指定
bunx guren openapi:generate --title "Blog API" --version "1.0.0"

# 出力パスを変更
bunx guren openapi:generate --out docs/openapi.json

# サーバー URL を含める
bunx guren openapi:generate --server "https://api.example.com"

# 既存ファイルを上書き
bunx guren openapi:generate --force

ランタイムでのドキュメントマウント

OpenAPI のスペックと、操作できるドキュメント UI を、アプリケーションから直接配信することもできます。

import { createApp } from '@guren/core'
import { mountOpenApiDocs } from '@guren/openapi'

const app = createApp({ routes: registerWebRoutes })

mountOpenApiDocs(app, {
  title: 'Blog API',
  version: '1.0.0',
})

次の 2 つのエンドポイントがマウントされます。

パス 説明
/openapi.json 生成された OpenAPI 3.1 JSON ドキュメント
/docs インタラクティブな API ドキュメント UI(Scalar)

パスは jsonPath と docsPath オプションで変更できます。

mountOpenApiDocs(app, {
  title: 'Blog API',
  version: '1.0.0',
  jsonPath: '/api/openapi.json',
  docsPath: '/api/docs',
})

Application のインスタンスにマウントする場合、ルート定義はルーターから自動で読み取られます。素の Hono のインスタンスにマウントする場合は、definitions を明示的に渡してください。

servers オプションには、配列のほかに関数も渡せます。マウントしたドキュメントはリクエストのたびに生成され、関数もそのつど呼ばれるので、マウントした時点ではまだ分からないアドレスも載せられます。たとえば PORT=0 の場合はポートを OS が割り当てるので、listen() が返るまでポート番号が決まりません。固定の配列を渡していると、ドキュメントも、そこから生成したクライアントも、何も待ち受けていないアドレスを指したままになってしまいます。

mountOpenApiDocs(app, {
  title: 'Blog API',
  version: '1.0.0',
  servers: () => [app.address?.url ?? 'http://localhost:3000'],
})

await app.listen({ port: 0 })

app.address は listen() がこのアプリをバインドしたアドレスで、バインドする前は undefined です。関数はリクエストのたびに呼ばれるので、listen() がアドレスを決めた後で読むことになります。素の Hono のインスタンスには問い合わせる Application がないので、そのアプリが知っている方法でアドレスを返してください。