Orval でカスタム fetch を使って JWT トークンを付与したリクエストを送る

OpenAPI クライアントの自動生成ツール Orval と、カスタマイズした fetch 関数を組み合わせることで、JWTトークンを自動でリクエストヘッダーに含めるメモです。

この方法により、生成されたAPIクライアントを使う際に、認証情報を意識する必要がなくなります。ここでは、認証に Supabase を利用している例を元に解説します。

Orval で custom-fetch を扱う

まず、すべての AP Iリクエストに先立って Supabase** **から現在のセッショントークンを取得し、Authorization ヘッダーに設定するカスタム fetch 関数を作成します。

import { supabase } from "@/lib/supabase"; // Supabaseクライアントのインポート

export const customFetch = async <T>(
  url: string,
  options?: RequestInit,
): Promise<T> => {
  // 1. Supabaseから現在のセッションを取得
  const {
    data: { session },
  } = await supabase.auth.getSession();

  // 2. 既存のヘッダーを複製
  const headers = new Headers(options?.headers);
  
  // 3. アクセストークンがあればAuthorizationヘッダーに設定
  if (session?.access_token) {
    headers.set("Authorization", `Bearer ${session.access_token}`);
  }

  // 4. APIのベースURLとリクエストURLを結合
  const requestUrl = `${process.env.EXPO_PUBLIC_API_URL}${url}`;
  
  // 5. APIリクエストを実行
  const response = await fetch(requestUrl, {
    ...options,
    headers, // トークン付きのヘッダーを適用
  });

  // 6. エラーハンドリング
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }

  // 7. レスポンスボディの処理 (204/205/304はボディなしとして処理)
  const body = [204, 205, 304].includes(response.status)
    ? null
    : await response.text();
  const data = body ? JSON.parse(body) : {};

  // 8. 必要な情報(データ、ステータス、ヘッダー)を返す
  return {
    data,
    status: response.status,
    headers: response.headers,
  } as T;
};

ポイント

  • セッション取得: supabase.auth.getSession()で現在のユーザーセッションを取得しています
  • トークン挿入: session?.access_tokenが存在する場合に、Authorization: Bearer 形式でヘッダーに設定しています。これがJWTトークンをAPIに渡す標準的な方法です
  • ベースURL: process.env.EXPO_PUBLIC_API_URLを使ってAPIのベースURLを付加しており、Orvalが生成するクライアント側では相対パス(/usersなど)での指定が可能になります

Orval 設定ファイルでの mutator の指定

次に、Orvalの設定ファイルで、生成されるAPIクライアントが標準のfetchの代わりに、作成したcustomFetch関数を使用するように指定します。

import { defineConfig } from "orval";

export default defineConfig({
  mobile: {
    output: {
      // ... その他の設定
      client: "fetch", // fetchクライアントを使用することを指定
      override: {
        mutator: {
          // customFetch関数へのパスと関数名を指定
          path: "src/lib/custom-fetch.ts", 
          name: "customFetch", 
        },
      },
    },
    input: {
      target: "http://localhost:3001/doc", // OpenAPIドキュメントの場所
    },
  },
});

client: "fetch": Orval に、fetch ベースのクライアントを生成するよう指定します。

この方法により、アプリケーション全体で認証されたAPIリクエストを簡潔かつ安全に行えるようになります。

著者

hiro08gh

hiro08gh (Hiroki Ueda)

フリーランスのフルサイクル / プロダクトエンジニア。プロダクト開発と犬が好き。Jamstack アーキテクチャーやサーバレス技術に興味があります。

Work Experience : MagicAl Pass、MONO Investment、microCMS、Commune...etc

SaaS 開発の経験が長く得意領域としています。業務において、ただ機能をリリースするだけではなく、「ユーザーが最大限使いやすいように考えること」、「長年使えるソフトウェアにすること」を大事にしています。

お仕事のお問い合わせは X (Twitter) の DM までお願いします。