9
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?

openapi-generator / openapi-generator-cli 小ネタ集

Last updated at Posted at 2025-09-19

はじめに

現在保守しているプロジェクトで openapi-generator および openapi-generator-cli を使っているので、それらに関わる小ネタをいくつか共有したいと思います。

最新の @openapitools/openapi-generator-cli を使おう

プロジェクトの依存する openapi-generator がいくら古かろうと、最新@openapitools/openapi-generator-cli を入れて問題ありません。

# 何も気にしないで最新版を入れればOK(常に最新にしてOK)
# (バージョンは https://www.npmjs.com/package/@openapitools/openapi-generator-cli?activeTab=versions を参照)
$ npm install -D @openapitools/openapi-generator-cli

ただし、このままだと生成されるコードが最新の openapi-generator 準拠になってしまうので、以下のコマンドでプロジェクトが依存する openapi-generator のバージョンを指定しましょう。

$ npx openapi-generator-cli version-manager set 5.4.0 

# 2系からコマンドは従来の openapi-generator から openapi-generator-cli になっています

あるいは package.json と同じフォルダに以下のような openapitools.json ファイルを放り込んでおけばOKです。先ほどのコマンドでも最終的には同様のファイルが生成されます。

openapitools.json
{
  "spaces": 2,
  "generator-cli": {
    "version": "5.4.0"
  }
}

// JSON Schemaの指定を行いたい場合は以下を指定しましょう
//   "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", (※ monorepoの場合パスが違うかもしれないので注意)
//   "$schema": "https://raw.githubusercontent.com/OpenAPITools/openapi-generator-cli/refs/heads/master/apps/generator-cli/src/config.schema.json" (※ URLで指定したい場合)

とにかくCLIツール自体は最新のものを使って構いません。設定でバージョンを古いものに固定すればいいのです。

OpenAPIを生成する元となるJSONは一旦ローカルにキャッシュとして保持しよう

CI上などでビルド時に動的にOAS定義を取得してソースコードを生成するのであれば別ですが、リポジトリ上に openapi-generator-cli が生成したソースコードをコミットしておく場合、 ソースコードだけではなく、元になったOAS定義のJSONも保存しておくと助かることが多いです。

生成元のJSONを保存しておくことで、生成元起因の定義の純粋な変更を観測できるようになり、openapi-generator自体の更新による影響の割り出しが行いやすくなります。

以下はスクリプトの例です。一旦 api:download でローカルにJSONを保存してから api:generate でそのJSONに基づきソースを生成しています。

package.json
"scripts": {
  "api": "npm run api:download && npm run api:generate",
  "api:download": "curl -o ./doc.json http://localhost:8080/api/doc.json",
  "api:generate": "openapi-generator-cli generate -i ./doc.json -g typescript-fetch -o ./src --skip-validate-spec"
}

時には doc.json を画面側の都合にあわせて部分的に修正したいこともあるでしょう。その場合は以下例の api:tweak のようなスクリプトを途中工程に挟めばいいです。ついでに、生成されたソースコードを改変したいケース(api:postprocess)もあり、最終的にこういう感じのスクリプトになることも多いです。

package.json
"scripts": {
  // 普通はもっと短く書けるのでプロジェクトに応じて調整してください
  "api": "npm run api:download && npm run api:tweak && npm run api:generate && npm run api:postprocess",

    "api:download": "curl -o ./doc.json http://localhost:8080/api/doc.json",
    "api:tweak": "node tools/tweaking-script.js ./doc.json ./doc.tweaked.json",
    "api:generate": "openapi-generator-cli generate -i ./doc.tweaked.json -g typescript-fetch -o ./src --skip-validate-spec",
    // 弊社PJのケースでいうと、日本語のenumがソースコードに出ないことがあり、ここで書き直したりしています
    "api:postprocess": "node tools/postprocess-script.js ./src"

  // あとフォーマッタを呼び出したりしてもいいかもしれない……
}

ちなみに --enable-post-process-file というツール公式のオプションがあり、ファイル単位で後処理を呼び出す場合には使えるのですが、このユースケースには使いづらいと思います。

気に入らないテンプレートは自作に差し替えよう

後のメンテの面倒を考えるとあまりお勧めしませんが、 ジェネレータの生成ソースコードが気に入らない場合、テンプレートとなる一連のMustacheファイルを本来のものとは別のものを指定することができます。

まずは編集元となるテンプレート(ここではtypescript-axios)を templates/my-typescript-axios にダウンロードしましょう。

$ npx openapi-generator-cli author template \
    -g typescript-axios \
    -o ./templates/my-typescript-axios

[main] INFO  o.o.codegen.cmd.AuthorTemplate - Extracted templates to './templates/my-typescript-axios' directory. Refer to https://openapi-generator.tech/docs/templating for customization details.

ダウンロードしたテンプレートについていくつか改変を行ったのち、上記のテンプレートフォルダを指定してソースコードを生成します。

$ npx openapi-generator-cli generate \
    -i ./doc.json -g typescript-axios \
    -o ./src --template-dir ./templates/my-typescript-axios --skip-validate-spec

あるいは公式マニュアルによれば、自分で AbstractTemplatingEngineAdapter を実装して、そのクラスのFQCNを指定かServiceProvider経由で自作テンプレートエンジンを呼び出せるようです。これは最終手段でしょう。

JavaScript系のAPIソースコードを生成する場合はシングルパラメータによる指定を好もう

古い typescript-* テンプレートがそうなのですが、しばしば GET メソッドのAPI生成について以下のような引数多めのソースコードを生成してしまいます。

src/api.ts
getDailyApplication(userId: string, year: number, month: number, day: number, options: any = {}) {
  // ...
}

こういったAPIの場合、API呼び出し側の year, month, day の順序が入れ替わってもおそらく誰も気づかないでしょう。一覧検索系のAPIなど、更に引数が多い場合、後々これが悪夢になるのは分かりきっています。

加えて、あるバージョンの SpringFox1 で観測しているのですが、OAS上のGETのパラメータ順序がランダムで入れ替わるケースを観測しています。(以下は実例ではないですが、イメージです)

呼び出し側のコード
// コード生成の度に引数が入れ替わるので 2025/4/5 が 2025/5/4 になったりする
getDailyApplication("userId", 2025, 4, 5)

このように、ことOASのAPI生成については引数の順序に依存したコードはバグの原因になるので、生成にあたっては単一プロパティの引数(useSingleRequestParameter=true)を有効にしましょう。

--additional-properties useSingleRequestParameter=true を追加
$ openapi-generator-cli generate \
    -i ./doc.json -g typescript-fetch \
    -o ./src --skip-validate-spec \
    --additional-properties useSingleRequestParameter=true

以下は生成結果と呼び出しコードです。

src/api.ts
export interface GetDailyApplicationRequest {
    userId: string;
    year: number;
    month: number;
    day: number;
}

getDailyApplication(requestParameters: GetDailyApplicationRequest, initOverrides?: RequestInit) {
  // ...
}
呼び出し側のコード
// 引数が名前付きになっている
getDailyApplication({
  userId: "userId",
  year: 2025,
  month: 4,
  day: 5,
})

既存の引数順序に依存したJS/TSのAPI呼び出しコードの書き換えについて

当然ですが、既存の引数順序に依存したコードを単一パラメータに変更した際、API呼び出し側のコードも上記のオブジェクトプロパティ渡しスタイルへ一斉に変更する必要があります。

私の場合は既存の引数順序とオブジェクトのプロパティをマッピングする定義を作成した上で(新旧のソースを読み込んで得られると思います)、 ts-morph を用いてAPI呼び出し部について一斉に書き換えました。

以下、かなり雑なソースコードですが、ts-morphを呼び出して書き換える例です。

API呼び出し部分のコードを書き換えるツールの実装(一部)
for (const n of src.getDescendantsOfKind(SyntaxKind.CallExpression)) {
    const propAccess = n.getFirstChild()?.asKind(SyntaxKind.PropertyAccessExpression)
    if (!propAccess) continue
    const callee = propAccess.getChildAtIndexIfKind(0, SyntaxKind.Identifier)
    if (!callee) continue
    const method = propAccess.getChildAtIndexIfKind(2, SyntaxKind.Identifier)
    if (!method) continue

    const apiName = apiConstMap[callee.getText()]
    if (!apiName) {
        continue
    }
    const methodName = method.getText()
    const interfaceName = `${apiName}${capitalizeFirstLetter(methodName)}Request`
    const foundInterface = getParamInterfaces[interfaceName]

    if (!foundInterface) {
        console.log("INTERFACE NOT FOUND", {interfaceName})
        continue
    }

    const objArgs: string[] = []
    const restArgs: string[] = []
    n.getArguments().forEach((arg, index) => {
        const propName = foundInterface[index]
        const prop = arg.getFullText()
        if (propName) {
            objArgs.push(`"${propName}": ${prop}`)
        } else {
            restArgs.push(arg.getFullText())
        }
        n.removeArgument(0)
    })
    n.addArgument(`{${objArgs.join(`,\n`)}}`)
    n.addArguments(restArgs)
}

あるいは今どきであれば、AIにこういったツールを作ってもらえばいいかもしれません。ASTをいじくるような、人間にとっては書き始めづらいツールのようなものでもササッと作ってくれることが多いです。

まとめ

openapi-generator および openapi-generator-cli について色々な小ネタを紹介してきました。

もうあまりOAS周りは活発ではないので、ここから進化することはあまり無いと思いますが、既存システムの保守改善で今回紹介したテクニックが役立てば幸いです。

  1. そもそもSpringFoxは2025年時点でメンテナンスされていないので、SpringDocに移行しましょう

9
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
9
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?