hacomono TECH BLOG

フィットネスクラブ・スクールなど施設・店舗のための会員管理・予約・決済システム「hacomono」 開発チームの技術ブログ

Nuxt3 の useFetch を OpenAPI ベースで型安全にしてみる

どうも、フロントエンドテックリードのみゅーとん(@_mew_ton)です。

TypeScript で型でガチガチに固めるのが好きで、よく業務時間内に型パズルを書いてます。

Nuxt3 の useFetch を OpenAPI ベースで型安全にする仕組みができたので、今回はその共有をします。

TL;DR

  • 記事中の Gist をそのままコピーして利用してください.
  • 以下の手順で型安全にする
    1. OpenAPI の定義ファイルを openapi-typescript をつかって TypeScript の型にする
    2. 型パズルを実装する
    3. useFetch でこの型を利用するためのラッパーを作る

対象読者

本記事は以下の読者を対象としています

  • Nuxt3 を使っていて、 useFetch を型安全にしたい要件がある
  • OpenAPI のスキーマをがんばってメンテできている
  • TypeScript で型ガチガチにしたい欲求がある

また、以下の内容については本記事では取り上げません

  • Nuxt3 の基礎, useFetch (ofetch) の使い方
  • 型パズルの解説

謝辞

社内向けのコードをマスキングした上で公開しているため、以下の解説をそのまま他のプロダクトへ適用できない可能性が高いかと思われます。

動作確認などはしていますが、完全でないことをご了承ください。

また、今回は Nuxt v3.5.0 を利用してコードを記載していますが、今後のアップデートにより使えなくなる可能性もあります。

OpenAPI のスキーマを型安全に参照する

前提条件

OpenAPI は前提として様々な構成で API スキーマを定義できる自由度はありますが、今回の仕組みでは、前提として OpenAPI が以下の条件で書かれていることが求められます。

  • データモデルに関する定義が、 APIの response 配下ではなく、 components/schemas 配下に定義され、再利用可能になっていること

型への変換

OpenAPI を型に変換する際には openapi-typescript を使用しました。

OpenAPI を型に変換するライブラリは他にも複数存在しますが、後述する型パズルを考慮し、最も都合のよいライブラリを選定しています。

基本的な使い方は、以下のコマンドを実行すればOKです

npx openapi-typescript ./path/of/input.openapi.yaml --output ./dist/schemas.ts

公式が用意している PetShop のAPIスキーマサンプルで実行すると、以下の Gist に示すような出力結果が得られます。

型パズルを追加

./dist/schemas.ts ができたら、以下の Gist にあるコードを追加してみてください。

型パズル自体の解説は行いません。 (解説が大変すぎる、かつ汎用的にしきれていないため)

少なくとも、以下の仕組みが提供されます。

  • 型のジェネリクス引数にて、API のパスとメソッドを指定すると、対応するリクエストデータ・レスポンスデータを取得できる
  • API のパスに変数を含む場合でも動作する (e.g. /pets/3 など)
  • 利用可能な API のパス一覧が型として提供される

OpenAPI を参照するコードのサンプル

さて、上記を組み込んだところで、以下のように OpenAPI を型として参照できるようになっているはずです。

import types { ResponseData } from './types'

type Pets = ResponseData<'/pets', 'get'>

しっかりサジェストが効くようになってます。

OpenAPI の型を使う useFetch のラッパーを作る

上記の型を nuxt で利用するために、 useFetch をラップする関数を作ります。

基本的には、以下の Gist のを参考に、 useFetch とは異なる形で型を再定義した useFetch2 を作ります。

使い方

useFetch の第1引数にはパスを入力しますが、ここまでの型変換がうまく行っていれば、 Visual Studio Code などの、TypeScript 補完が効くエディターで補完させてみると、効いているのがわかると思います。

(2023/08/18 追記) パスパラメータを含むパスを選択した後、パラメータ部分を直接値に変更します。 OpenAPI 側で、パラメータのタイプが string か number か、など正しく型定義されていれば、パスパラメータ部分が値に変わっても、エラーになりません。

第2引数でメソッドを指定しますが、こちらも効いてます。

レスポンスデータの型も、引数に合わせて自動的に保管されます。

まとめ

さて、今回は OpenAPI のスキーマを useFetch で型安全に利用する方法を示しました。

useFetch の度に利用可能な API がサジェストされるため、開発効率はかなり良くなりました。

ただし、これはあくまで暫定的な対応として有用ではないかと思います。 理想は、 OpenAPI を入力すれば、Nuxt3 が内部で自動的に作る型として、 .nuxt フォルダ配下に型が作られるような構成が望ましいかなと思います。

個人的には、これをどうやって OSS 化するかで、今非常に悩んでいます。有識者の方、なにかアドバイスいただければと思います。


株式会社hacomonoでは一緒に働く仲間を募集しています!
採用情報や採用ウィッシュリストも是非ご覧ください!