8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Hono × Zod OpenAPI × Swagger UIでAPIドキュメント簡単自動生成

8
Last updated at Posted at 2026-06-15

はじめに

前回に引き続き、Hono関連の記事を書きました。

Honoでは OpenAPI 定義と API 実装を同じ場所で管理できます。
さらに Swagger UI を組み合わせることで、ブラウザから API ドキュメントの閲覧や動作確認も可能になります。

この記事では Hono + Zod OpenAPI + Swagger UI を使った最小構成を紹介します。

本文に入る前に、各ツールの役割を整理しておきます。

  • OpenAPI → API仕様を記述するための標準フォーマット
  • Swagger UI → OpenAPI仕様書をブラウザで閲覧・実行できるツール
  • Zod OpenAPI → ZodスキーマからOpenAPI仕様書を生成する仕組み

検証環境

  • Node.js: v20.x
  • パッケージマネージャー: npm

セットアップ

まずはベースとなるHonoのプロジェクトを作成します。

npm create hono@latest hono-openapi-demo

今回はランタイムにNode.jsを使うため、テンプレートはnodejsを指定します。

✔ Using target directory … hono-openapi-demo
✔ Which template do you want to use? nodejs
✔ Do you want to install project dependencies? Yes
✔ Which package manager do you want to use? npm
✔ Cloning the template
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd hono-openapi-demo

次に、今回使用するZod OpenAPIとSwagger UIのミドルウェアをインストールします。

npm install @hono/swagger-ui @hono/zod-openapi

次の章から、まずは最もシンプルな方法でSwagger UIを表示させ、その後にZod OpenAPIを使って実装とドキュメントを連動させる形に拡張します。

Swagger UIの導入

まずはZod OpenAPIを使わず、HonoにSwagger UIのミドルウェアだけを組み込んでみます。書籍管理アプリのAPIを想定したサンプルです。

src/index.ts の内容を以下のように書き換えます。

import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { swaggerUI } from '@hono/swagger-ui'

const app = new Hono()

// 手動で定義したAPI仕様(JSON)を返すエンドポイント
app.get('/doc', (c) => {
  return c.json({
    openapi: '3.0.0',
    info: {
      title: 'Book API Sample',
      version: '1.0.0',
      description: 'Honoで作成した書籍管理APIのサンプル',
    },
    paths: {
      '/books/{id}': {
        get: {
          summary: '書籍情報の取得',
          description: '指定されたIDに一致する書籍の情報を取得します。',
          parameters: [
            {
              name: 'id',
              in: 'path',
              required: true,
              schema: {
                type: 'string',
              },
              description: '書籍の識別ID',
            },
          ],
          responses: {
            200: {
              description: '成功',
            },
          },
        },
      },
    },
  })
})

// Swagger UIの表示用エンドポイント
app.get('/ui', swaggerUI({ url: '/doc' }))

serve({
  fetch: app.fetch,
  port: 3000
}, (info) => {
  console.log(`Server is running on http://localhost:${info.port}`)
})

ここで一度、npm run devでサーバーを起動し、http://localhost:3000/uiにアクセスしてみます。このように、Swagger UIのドキュメントを確認することができます。

image.png

ここではあくまでドキュメントを生成しただけで、実際の/books/{id}エンドポイントができたわけではありません。そこへ実際にリクエストを送信しても、Hono側に処理が書かれていないため404エラーが返ってきます。

また、このままではAPI実装を追加・変更しても、その度にOpenAPI仕様(JSON)をすべて手書き修正することになります。

そこで次にZod OpenAPIを導入します。

Zod OpenAPIで拡張する

手書きしていたAPI仕様を、Zodのスキーマから自動生成できるように拡張します。

  • 通常版のHonoクラスからOpenAPIHonoクラスに切り替える

  • zオブジェクトを使ってスキーマを定義

  • ルート(/books/{id})の定義

import { serve } from '@hono/node-server'
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'
import { swaggerUI } from '@hono/swagger-ui'

const app = new OpenAPIHono()

// パラメータのスキーマを定義
const ParamsSchema = z.object({
  id: z.string().openapi({
    param: { name: 'id', in: 'path' },
    example: '123',
    description: '書籍の識別ID',
  }),
})

// レスポンスのスキーマを定義
const BookSchema = z
  .object({
    id: z.string().openapi({ example: '123', description: '書籍ID' }),
    title: z
      .string()
      .openapi({ example: 'Hono入門', description: '書籍のタイトル' }),
    author: z.string().openapi({ example: '山田太郎', description: '著者名' }),
    publishedAt: z
      .string()
      .openapi({ example: '2026-01-01', description: '出版日' }),
  })
  .openapi('Book')

// 404エラー時のレスポンススキーマ
const ErrorSchema = z.object({
  message: z.string().openapi({ example: 'Not Found' }),
})

// ルート(API仕様)の定義
const getBookRoute = createRoute({
  method: 'get',
  path: '/books/{id}',
  summary: '書籍情報の取得',
  description: '指定されたIDに一致する書籍の情報を取得します。',
  request: {
    params: ParamsSchema,
  },
  responses: {
    200: {
      content: { 'application/json': { schema: BookSchema } },
      description: '指定した書籍の情報を返します',
    },
    404: {
      content: { 'application/json': { schema: ErrorSchema } },
      description: '書籍が見つかりませんでした',
    },
  },
})

const dummyBooks = [
  {
    id: '123',
    title: 'Hono入門',
    author: '山田太郎',
    publishedAt: '2026-01-01',
  },
  {
    id: '456',
    title: 'HonoによるWeb API開発',
    author: '佐藤次郎',
    publishedAt: '2026-05-15',
  },
]

// ルート定義とハンドラーの紐付け
app.openapi(getBookRoute, (c) => {
  const { id } = c.req.valid('param')
  const book = dummyBooks.find((b) => b.id === id)

  if (!book) {
    return c.json({ message: 'Not Found' }, 404)
  }

  return c.json(book, 200)
})

app.doc('/doc', {
  openapi: '3.0.0',
  info: {
    title: 'Book API Sample',
    version: '1.0.0',
    description:
      'HonoとZod OpenAPIで自動生成した書籍管理APIのドキュメントです。',
  },
})

app.get('/ui', swaggerUI({ url: '/doc' }))

serve(
  {
    fetch: app.fetch,
    port: 3000,
  },
  (info) => {
    console.log(`Server is running on http://localhost:${info.port}`)
  },
)

これで自動的にスキーマまで反映された仕様書が完成します。再度ブラウザでhttp://localhost:3000/uiにアクセスします。

今度はZodで定義した通りのデータ構造や、各項目のdescription、データのexampleが自動的にSwagger UIへ反映されていることが分かります。

image.png

今回はドキュメントだけでなく実際のAPI処理も紐づいているため、Try it out から id に 123 を入力して実行すれば、実際にダミーデータが返ってきます。

この構成にすることで、APIのバリデーション、実際の処理、APIドキュメントの3つが常に一箇所で管理され、仕様書が古くなる問題を防ぐことができます。

参考

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?