📝 雑な日記と備忘録

2025/04/27 更新

orval入門:OpenAPIスキーマを別リポジトリから取り込んでAPIクライアントを自動生成する手順

バックエンドとフロントエンドのリポジトリが分かれている状況でAPIスキーマを同期する必要に駆られていい感じのフローを構築したので備忘録として残しておきます。

まあこういう状況って結構あると思ってる&ベストプラクティスとかはそのチームによって違うのかなとも思っています

なので、この記事に書かれていることは日本のどっかの開発チームが採用した手法レベルで読んでもらえると嬉しいです😜

まず初めてに簡単にフローを説明しておくと簡単2ステップです

  • ghコマンドを使って別リポジトリのファイルを取り込む
  • 取り込んだファイルを元にorvalで自動生成

比較的簡単に構築できるのでいろんなプロジェクトで使いまわせるのがポイントですね😋

では本題へ行きましょう!

前提

インストールしておいて欲しいもの

  • react-query
  • axios
  • tsx
  • orval

です!

それぞれの使い所は後述します!

別リポジトリからOpenAPIファイルを取り込む

まずOpenAPIファイルを取得するためのスクリプトを作成していきます。

import { execSync } from "child_process";
import fs from "fs";

const OPENAPI_DIR = "openapi";
const OPENAPI_FILE = "swagger.yml";
const API_PATH =
  "/repos/{ORG名}/{リポジトリ名}/contents/{ファイルの保存パス}";

async function main() {
  try {
    // openapiディレクトリが存在しない場合は作成
    if (!fs.existsSync(OPENAPI_DIR)) {
      fs.mkdirSync(OPENAPI_DIR, { recursive: true });
    }

    // OpenAPIファイルをダウンロード
    execSync(
      `gh api -H "Accept: application/vnd.github.raw" "${API_PATH}" > ${OPENAPI_DIR}/${OPENAPI_FILE}`,
      { stdio: "inherit" }
    );

    // クライアントを生成
    console.log("🔨 Generating client...");
    execSync("pnpm gen:client", { stdio: "inherit" });
  } catch (error) {
    console.error("❌ Error:", error);
    process.exit(1);
  }
}

main();

このファイル(ファイル名、ディレクトリ構成はお任せします)を作成します

ここで1つポイントがあるとすると、スクリプトをtsファイルで作成するとこですかね

当初はshファイルで作成していたんですがshファイルだとシステムの権限付与のコマンドを実行する必要があります、まあこれはめんどくさい。

READMEで書くと解決できるかなと思ったんですが、それだとちょっと認知不可が上がってしまう。

認知不可をちょっとでも下げる目的でtsファイルで作成しました

次にこのファイルを実行するためのコマンドを`package.json`に記述します

"fetch:apiSchema": "tsx ./scripts/fetchApiSchema.ts"

これで実行するためのファイルとコマンドの作成が完了しました!お疲れ様です〜

次にorvalの設定です

orval.config.tsを作成

orvalはhooksやmockも作成してくれるので、自動生成コマンド実行者の環境などによって生成結果が違ってしまうと困るので誰が実行しても同じ生成結果にするために作成します

で、生成内容は一体何?!っていうところなのですが僕のチームでは以下を生成しています

  • hooks
  • mock

api client(axios)の生成からhooks(react-query)まで自動生成してもらうようにしています。

mockはStorybookを作成している関係で、mockの生成もしてもらっています。

他にもオプションがいろいろあるみたいなのでドキュメントを読んでみてください!

https://orval.dev/

で、具体的にどんな内容を設定しているかですが以下になります

import { defineConfig } from "orval";

export default defineConfig({
  api: {
    input: {
       target: {ターゲットOpenAPIファイル},
    },
    output: {
       target: "./src/api/client.generated.ts",
       namingConvention: "PascalCase",
       schemas: "./src/api/model",
       client: "react-query",
       mock: true,
    },
  },
})

ここまで作成し切ったら

先ほどの自動生成のコマンドを実行しましょう!

以下のような出力がターミナルに表示されたら構築完了です〜!