🙆

hono-openapiでOpenAPI Specを生成する

2025/01/22に公開

はじめに

Honoを使って OpenAPI を生成する際、ネットをさまよっていると@hono/zod-openapiを使われる方が多いように見受けられます。

しかしながら個人的にいくつか不満点があったため、今回はhono-openapiを試してみました。

hono-openapi

hono-openapi@hono/zod-openapiと同じく OpenAPI Spec の生成を自動化するミドルウェアツールです。@hono/zod-openapiと比べて以下のような特徴があります。

  • zodに限らず、ValibotArkTypeを含む多様なスキーマライブラリを使用できる。
  • Hono の基本クラスを使うことをベースとしている。

https://github.com/rhinobase/hono-openapi

@hono/zod-openapiへの不満点

個人的な感想として@hono/zod-openapiへは以下のような不満点がありました。
少なからず以下の課題はhono-openapiで解決できました。

  • OpenAPIHonoクラスを使用する必要がある
    • 機能だけならHonoクラスを使えるが、OpenAPI 生成を必要とする場合このクラスを利用する必要がある
  • パスやメソッドに一覧性がない
    • どのメソッドを定義する場合でもroute.openapi()の形式で定義する必要があり、route 定義の変数名を工夫するなどしないとそのファイル内だけでメソッドがわかりにくい

使い方

ここからはhono-openapiの使い方を紹介していきます。

route定義

定義自体は@hono/zod-openapiとそれほど変わりませんが、hono-openapiではパスやメソッドが含まれません。
あくまで補足的な形での定義となっており、可読性が損なわれないと感じます。

import { Hono } from "hono";
import { describeRoute } from "hono-openapi";
import { resolver } from "hono-openapi/zod";

// ドキュメントを定義
const route = describeRoute({
  description: "Say hello to the user",
  responses: {
    200: {
      description: "Successful response",
      content: {
        "text/plain": { schema: resolver(responseSchema) },
      },
    },
  },
});

// routeを登録
const app = new Hono();
app.get("/", route, (c) => {
  // ...
});

validation

現状、body, query等のリクエストパラメータはスキーマライブラリを使用して定義はできません。
これらのドキュメントはスキーマライブラリごとに用意されるValidatorを追加すると自動で登録されます。

import { validator as zValidator } from "hono-openapi/zod";

app.get(
  "/{id}",
  route,
  zValidator("param", paramSchema), // validatorを追加
  (c) => {
    const { id } = c.req.valid("param");
    // ...
  }
);

レスポンスもデフォルトでは検証されませんが、validateResponseを有効にすれば検証が可能です。

const route = describeRoute({
  // ...
  validateResponse: true, // ← レスポンスの検証を有効化
});

OpenAPI Spec を生成

@hono/zod-openapiと同じく OpenAPI Spec の生成・Swagger UI等でのホストが可能です。
同じサーバーでホストしたくない場合は、オブジェクトとして出力し別サーバーでのホストも可能です。

import { generateSpecs, openAPISpecs } from 'hono-openapi';
import { Hono } from 'hono';
import { swaggerUI } from '@hono/swagger-ui';

const app = new Hono();

const documentation = {
  openapi: '3.1.0',
  info: {
    title: 'My API',
    version: '0.0.1',
  },
};

// オブジェクトとして出力
generateSpecs(app, { documentation });

// ドキュメントをホスト
app.get('/doc', openAPISpecs(app, { documentation }));

その他

describeRoutehideプロパティを設定することで特定の条件でのみrouteを有効化できます。

const route = describeRoute({
  // ...
  hide: process.env.NODE_ENV === "production", // ← 本番環境ではこのルートはアクセスできない
});

注意点

zod-openapiの代用で@hono/zod-openapiは使用できません

hono-openapiでも@hono/zod-openapiと同様に拡張されたzodを利用する必要があります。
どちらもzod-openapiをベースとしていますが、内部的に微妙な違いがあるようで.openapi()での挙動が異なります。
hono-openapiを利用する場合は、レポジトリに記載の通りzod-openapi/extendを読み込む形式で使用しましょう。

https://github.com/rhinobase/hono-openapi#setting-up-your-application

まとめ

今回はhono-openapiを紹介しました。
生のHonoクラスの書き味をそのままに、定義を追加できる手法が個人的に好みです。
私は hono 歴がまだまだ浅いので hono についても知見をためて行きたいです。

Discussion