SmartHR Tech Blog

SmartHR 開発者ブログ

rspec-openapi × Orval で API クライアント生成を自動化 —— AI Agent で移行する

スキル・学習管理チームでプロダクトエンジニアをしている yamaguchi です。

本記事では、自前実装していたAPIクライアント(SWR + カスタムfetcher)を、rspec-openapiOrvalを組み合わせて自動生成されたAPIクライアントに移行した取り組みについて紹介します。

移行に至った背景、ライブラリ選定時の比較検討、そしてAI Agentを活用した移行作業の方法まで、ざっくりとお話しします。

なぜ移行することにしたか

スキル・学習管理のプロダクトでは、Ruby on Rails(バックエンド)と Next.js(フロントエンド)の構成を採用しています。フロントエンドではSWRを使ってデータの取得/更新を管理しており、APIクライアントはSWRとプロダクト内で定義しているカスタムfetcherを組み合わせて自前で実装していました。
バックエンドで新しいAPIが作成されるたびに、フロントエンド側でも対応するAPIクライアントを手動で実装する必要がありました。

このようなAPIクライアントを自前で実装するアプローチには以下のような課題がありました。

[実装面での課題]

  • エンドポイントごとに、SWRをラップする関数の作成や、モックデータが多い
  • レスポンスの型定義が誤っていても気づくことができない
    • 型定義上ではundefinedを期待しているが、実際はnullが返却される箇所が存在する

[運用面での課題]

  • 実装する人の裁量でデータ操作の配置がばらつき、どのファイルに実装されているか把握しづらい

OpenAPI から自動生成される API クライアントに移行することで、これらの課題を解決できると考え、移行する決断をしました。

移行するにあたり、まずはライブラリ選定から始めていきました。 そのうえで、OpenAPIの生成をどうするか、OpenAPIからAPIクライアントの生成をどうするか、の2点を検討する必要がありました。

OpenAPI生成ライブラリの選定

まず1つ目は、OpenAPIの生成をどうするか、です。
前提として、スキル・学習管理チームでは、rspec-openapiを使い、request specからOpenAPIを自動生成していました。
今回はそれを引き続き使用することにしました。

rspec-openapiとは

rspec-openapiは、RSpecのrequest specから自動でOpenAPIを生成するライブラリです。 テストケースを実行することで、ある程度実際のAPIの挙動に基づいたOpenAPIを作成できます。

github.com

なぜrspec-openapiを継続利用することにしたか

すでに導入されていて、チームでの使用実績もあるので、まずはrspec-openapiを継続使用することにしました。
懸念点としては、rspec-openapiでは、OpenAPIの生成方法の都合上、request specの網羅性に依存するため、実際のAPIとずれが生じる可能性があります。
それは、テストケースの不足による実装時の考慮不足が根本的な原因であり、 request spec の拡充が必要なケースのはず、と考えました。
そのため、追加のライブラリの導入や別の解決策への移行などはせず、rspec-openapi単体で運用してみることにしました。

APIクライアント生成ライブラリの選定

次に検討したのは、OpenAPIからAPIクライアントの生成をどうするか、です。
結論として、APIクライアント生成ライブラリにはOrvalを選定しました。

Orvalとは

Orvalとは、 OpenAPI をもとに、型安全な TypeScript コードを生成することができるライブラリです。

orval.dev

要件

なぜOrvalを選定したのでしょうか。
ライブラリの選定をするためにも、まずは要件の整理から始めました。選定にあたり、以下のように要件を定義してみました。

  • SWR と組み合わせて使用できること
    • SWRに依存した自作の関数でキャッシュ操作を行っています。それを引き続き使用したいのでSWRを使いたいです。
  • カスタムfetcherを使用できること
    • 既存のカスタムfetcherでエラーハンドリングを実装しているため、その仕組みを維持したいです。
  • MSW モックの自動生成ができること
    • コンポーネントテストで MSW を使っているので、モックの自動生成できると楽できて嬉しいです。
  • SmartHR 社内での使用実績があること
    • 導入にあたり、懸念点などを聞けると不安がないので社内での導入実績があるとより良かったです。

現状のコードと様々なライブラリの特性の両方を見ながら要件を洗い出すと、程よい感じに要件が定義できました。

比較

次にライブラリを調査して、リストアップし、星取表を作成しました。

ライブラリ / リポジトリ SWRとの組み合わせ MSW 自動生成 カスタムfetcherの使用 SmartHR社内実績
OpenAPI-generator △ (公式非対応) × ×
aspida + openapi2aspida ○ (@aspida/swr) × ×
openapi-zod-client × × ×
openapi-typescript + openapi-fetch ○ (openapi-msw) あり
Orval ◎ (@orval/swr) あり

Orvalに決定した理由

今回の要件をすべて満たし、単体で完結するOrvalを採用することにしました。

各ライブラリの特徴をまとめると、

  • OpenAPI-generatorSWRの公式サポートがなく、今回の要件には適していませんでした。

  • aspida + openapi2aspidaは柔軟性が高そうな一方で、aspida用に修正が必要な箇所が多くみられ導入コストが高そう、かつMSWモックの自動生成に対応していない点が課題でした。

  • openapi-zod-clientZodを前提としており、現在のプロジェクトでZodを使用していないため選外となりました。

  • openapi-typescript + openapi-fetchは軽量で拡張性が高く、良い選択肢でしたが、SWRのラップ機能でOrvalのほうが素直に使用できそうでした。

openapi-typescript + openapi-fetchも要件は満たしていたのですが、今回は単体で完結できる Orval を選定しました。

置き換えにはCursor RuleやDevinを使用

手動でAPIクライアントを一つずつ置き換えていくのは非常にめんどくさい作業です。そこで、できるだけ楽ができないかを考えました。 弊チームではIDEにCursorを使用しているメンバーが多いため、まず最初にCursor Ruleを活用した置き換え作業で自動化できないかを検討しました。

AI Agentに作業を実施してもらうためには、実施する作業フローとその詳細をしっかり記載して、教えてあげなくてはいけません。 なので、

1. まずは置き換え方の流れを考えました。

Step1: 既存のAPIクライアントと自動生成されたAPIクライアントを比較
↓
Step2: 差があれば、request specを修正 & OpenAPIを再生成
↓
Step3: APIクライアントを生成し、置き換え
↓
Step4: コンポーネントテストがなければ作成、テスト実施
↓
終わり

2. ここから各ステップごとに、実施する作業内容を詳細化します。

Step1: 既存のAPIクライアントと自動生成されたAPIクライアントを比較
今使っているAPIクライアントの型と自動生成されている型で、プロパティに差があるかを確認してください。
ex ) ~

Step2: 差があれば、request specを修正 & OpenAPIを再生成、差がなければStep3へ
差がある場合は、backend/spec/requestsを確認してテストを追加修正してください。テストの書き方は[テストの書き方のルール]を確認してください。
修正ができたら、以下のコマンドを実行してOpenAPIを生成してください。
OPENAPI=1 bin/rspec spec/requests

Step3: APIクライアントを生成し、置き換え

etc ....

3. このプロンプトを繰り返し改善し、必要な指示を追加したり不要な部分を削除したりしながら精度を高めていきました。

以下が上記までをまとめたプロンプト改善フローです。

flowchart LR
    subgraph SG1 ["プロンプト作成"]
        A[作業フローの設計] --> B[詳細手順の記述]
    end
    B --> C[Cursor Agentで実行]
    C --> D{期待通りの結果?}
    D -->|Yes| E[完了]
    D -->|No| F[問題点を特定]
    
    subgraph SG2 ["プロンプト改善"]
        G[不要な指示を削除]
        H[必要な指示を追加]
    end
    
    F --> SG2
    SG2 --> C

ステップ数まあまあ多く、これならWorkflow化したほうがうまくいくのでは?という気持ちを抑え、Cursor Agentに置き換え作業を実施してもらいました。
結果として、予想以上にうまくワークしました。少し懸念していたRSpecの修正作業も想像以上にうまく修正してくれていました。

このような置き換えタスクは、やるべきことが比較的明確な場合が多く、フィードバックサイクルが長くても良いタスクです。
フィードバックサイクルが長くてもいいタスクに適しているAI Agentといえば Devin さんです。
Devin さんは成果物がでてくるまでの時間が大変長く、普段遣いが難しい御方です。
しかし、このようなフィードバックサイクルが長くても良いタスクはまあまあ向いていると思っています。
そこで現在は、DevinのPlaybookを使った置き換え作業の自動化を検証しています。

置き換え時に発生した問題

置き換え作業を進める中で、rspec-openapiで生成されるOpenAPIの品質に課題があることが分かりました。 当初は、テストケースの不足による実装時の考慮不足が主な原因だと考えていました。しかし、実際にはテストを書いているにもかかわらず、OpenAPIに正しく反映されていない箇所がいくつか見つかりました。 そのため、OpenAPIの生成がうまくいくように、特にロジックのないテストケースや冗長なテストを追加することで回避していました。

最初はこの方法で問題ないと考えていましたが、そのようなケースが多く発生し、無視できない状況になってきました。 テストが冗長になることは、保守性の観点から避けたい問題です。

そこで現在検討しているものの1つが、OpenAPIを直接編集するアプローチです。

Furthermore, rspec-openapi keeps manual modifications when it merges automated changes to OpenAPI specs in case we can't generate everything from request specs.
(日本語訳) さらに、rspec-openapiは自動化された変更をOpenAPI仕様に反映させる際にも、手動で加えた修正を保持します。 これは、リクエスト仕様から完全に仕様を生成できない場合に備えての機能です。 https://github.com/exoego/rspec-openapi#whats-this

つまりrspec-openapiは、RSpecのrequest specからOpenAPIを生成できますが、OpenAPIを手動で修正しても問題ないように設計されています。

まだ検討中なので他の手段になるかもしれません。これからチームでいい解決策が見つけていければと思います!

We Are Hiring!

SmartHR では一緒に SmartHR を作りあげていく仲間を募集中です!

少しでも興味を持っていただけたら、カジュアル面談でざっくばらんにお話ししましょう!

hello-world.smarthr.co.jp