mcpField
D
data-client-rest-setup

data-client-rest-setup

github.com/reactive開発2.0k36 回インストール今日更新
リポジトリ

data-client-rest-setup について

REST API を使うプロジェクトで `@data-client/rest` を設定・移行するためのスキルです。既存の HTTP ライブラリを特定して移行を案内し、プロジェクト固有のエンドポイント基底クラスの設計を支援します。

主な機能

  1. 01既存 HTTP ライブラリの特定
  2. 02Axios、fetch、Ky、SuperAgent、Got からの移行案内
  3. 03カスタム RestEndpoint 基底クラスの設定
  4. 04resource、endpoint、schema の利用案内

活用シーン

  1. 01アプリを Axios または fetch から移行
  2. 02新規プロジェクトへの REST 対応追加
  3. 03endpoint の認証とレスポンス処理の統一

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/reactive/data-client --skill data-client-rest-setup

インストール後、Claude Code で/data-client-rest-setupを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
axios-migration.md
fetch-migration.md
got-migration.md
ky-migration.md
superagent-migration.md

詳細

REST プロトコルのセットアップと移行

このスキルでは、プロジェクト向けに @data-client/rest を設定します。新規セットアップと既存の HTTP ライブラリからの移行の両方に対応しています。「data-client-setup」スキルで REST API のパターンが検出された後に適用してください。

まず、正確な実装パターンを確認するため、「data-client-rest」スキルを適用してください。

ステップ 1:インストール

コアパッケージとあわせて REST パッケージをインストールします。

# npm
npm install @data-client/rest

# yarn
yarn add @data-client/rest

# pnpm
pnpm add @data-client/rest

ステップ 2:既存の HTTP パターンを検出する

コードベースをスキャンして、現在使用されているものを確認します。複数のパターンが共存している場合があります。該当する移行サブ手順をそれぞれ、関連するファイルに対して個別に実行してください。

検出チェックリスト

package.json の依存関係を確認し、ソースファイルをスキャンします。

確認項目パターン対応
依存関係に "axios" がある、またはソースに import.*from ['"]axios['"] があるAxiosreferences/axios-migration.md に従ってください
REST スタイルの URL を使った fetch( 呼び出し、または fetch をラップする関数があるRaw fetchreferences/fetch-migration.md に従ってください
依存関係に "ky" がある、または import.*from ['"]ky['"] があるKyreferences/ky-migration.md に従ってください
依存関係に "superagent" があるSuperAgentreferences/superagent-migration.md に従ってください
依存関係に "got" がある(ブラウザーコードではまれ)Gotreferences/got-migration.md に従ってください
既存の HTTP ライブラリが検出されない新規プロジェクトステップ 3:カスタム RestEndpoint 基底クラス に進んでください
判別が難しい場合

使用されているパターンを確実に特定できない場合(例:明確なインポートはないものの、HTTP 呼び出しがある場合)は、ユーザーに確認してください。

コードベースで HTTP 呼び出しを見つけましたが、使用されているライブラリを特定できませんでした。次のどれから移行しますか?

  1. axios
  2. Raw fetch/カスタム fetch ラッパー
  3. ky
  4. superagent
  5. その他(説明してください)
  6. 新規に開始(移行は不要)
複数のライブラリが混在するコードベース

複数の HTTP ライブラリが検出された場合は、関連するファイルに対して各サブ手順を実行します。サブ手順は個別に実行でき、互いに競合しません。

  1. どのファイルでどのライブラリが使われているかを特定します(インポート文ごとに分類します)
  2. 該当する移行サブ手順をそれぞれ、対象のファイル群に対して実行します
  3. すべての移行が完了したら、基底クラスのセットアップに進みます

移行リファレンス

各移行リファレンスは、それ単体で完結しています。上記の検出結果に基づいて、該当するものだけを読みます。移行が完了したら、ここに戻って基底クラスをセットアップしてください。

  • Axios → references/axios-migration.md — codemod、インターセプター、エラー処理、タイムアウト、cancelToken、responseType、paramsSerializer、認証、validateStatus、CSRF、アップロードの進捗状況
  • Raw fetch → references/fetch-migration.md — fetch ラッパー、ヘッダー、ステータスチェック、POST パターン、エラー処理
  • Ky → references/ky-migration.md — prefixUrl、フック、HTTPError、インスタンス設定
  • SuperAgent → references/superagent-migration.md — チェーン型 API、プラグイン、エージェント、ファイルアップロード
  • Got → references/got-migration.md — Node.js のパターン、フック、ページネーション、再試行

ステップ 3:カスタム RestEndpoint 基底クラス

インストールと必要な移行が完了したら、プロジェクト用のカスタム RestEndpoint クラスの作成を提案します。

検出チェックリスト

一般的な REST パターンを含めるため、既存のコードベースを調べます。

  1. ベース URL/API プレフィックス:https://api.example.com のようなハードコードされた URL や、process.env.API_URL のような環境変数を探します
  2. 認証:Authorization ヘッダー、localStorage/Cookie 内のトークン、認証インターセプターを探します
  3. Content-Type の処理:API が JSON、form-data、カスタム Content-Type のどれを使用するか確認します
  4. エラー処理:エラーレスポンスのパターンやステータスコードの処理を探します
  5. リクエスト/レスポンスの変換:データ変換、日付の解析、ケース変換を確認します
  6. クエリ文字列の形式:単純なパラメーターかネストされたオブジェクトかを確認します(qs ライブラリが必要になる場合があります)
ベースクラスのテンプレート

src/api/BaseEndpoint.ts にファイルを作成します(またはプロジェクト構成に応じた場所に作成します)。

import { RestEndpoint, RestGenerics } from '@data-client/rest';

/**
 * Base RestEndpoint with project-specific defaults.
 * Extend this for all REST API endpoints.
 */
export class BaseEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
  // API base URL - adjust based on detected patterns
  urlPrefix = process.env.REACT_APP_API_URL ?? 'https://api.example.com';

  // Add authentication headers
  getHeaders(headers: HeadersInit): HeadersInit {
    const token = localStorage.getItem('authToken');
    return {
      ...headers,
      ...(token && { Authorization: `Bearer ${token}` }),
    };
  }
}

一般的なライフサイクルのオーバーライド

コードベースでの検出結果に基づき、以下を含めます。API ドキュメントの全容については、RestEndpoint を参照してください。

認証(非同期のトークン更新)
async getHeaders(headers: HeadersInit): Promise<HeadersInit> {
  const token = await getValidToken(); // handles refresh
  return {
    ...headers,
    Authorization: `Bearer ${token}`,
  };
}
React コンテキストからの認証(Okta、Auth0)

認証トークンが React コンテキスト内にあり(localStorage にはない場合)、ベースクラスの getHeaders() からトークンにアクセスすることはできません。hookifyResource() を使って、コンテキストから取得したヘッダーをすべてのエンドポイントに注入します。

import { hookifyResource, resource } from '@data-client/rest';

const ArticleResourceBase = resource({
  path: '/articles/:id',
  schema: Article,
  Endpoint: BaseEndpoint,
});

export const ArticleResource = hookifyResource(
  ArticleResourceBase,
  function useInit() {
    const accessToken = useContext(AuthContext);
    return {
      headers: { Authorization: `Bearer ${accessToken}` },
    };
  },
);

使用例:useSuspense(ArticleResource.useGet(), { id }) — フックはレンダーのたびに useInit() を呼び出すため、トークンは常にコンテキストから最新のものが取得されます。

カスタムリクエスト初期化(CSRF、認証情報)
getRequestInit(body?: RequestInit['body'] | Record<string, unknown>): RequestInit {
  return {
    ...super.getRequestInit(body),
    credentials: 'include', // for cookies
    headers: {
      'X-CSRF-Token': getCsrfToken(),
    },
  };
}
カスタムレスポンス解析(データのエンベロープを解除)
process(value: any, ...args: any[]) {
  // If API wraps responses in { data: ... }
  return value.data ?? value;
}
カスタムエラー処理
async fetchResponse(input: RequestInfo, init: RequestInit): Promise<Response> {
  const response = await super.fetchResponse(input, init);
  
  if (response.status === 401) {
    window.dispatchEvent(new CustomEvent('auth:expired'));
  }
  
  return response;
}
カスタム検索パラメーター(qs ライブラリを使用)
searchToString(searchParams: Record<string, any>): string {
  return qs.stringify(searchParams, { arrayFormat: 'brackets' });
}
カスタム parseResponse(JSON 以外を処理)
async parseResponse(response: Response): Promise<any> {
  const contentType = response.headers.get('content-type');
  
  if (contentType?.includes('text/csv')) {
    return parseCSV(await response.text());
  }
  
  return super.parseResponse(response);
}

複数のオーバーライドを含む完全な例

import { RestEndpoint, RestGenerics } from '@data-client/rest';
import qs from 'qs';

export class BaseEndpoint<O extends RestGenerics = any> extends RestEndpoint<O> {
  urlPrefix = process.env.API_URL ?? 'http://localhost:3001/api';

  async getHeaders(headers: HeadersInit): Promise<HeadersInit> {
    const token = await getAuthToken();
    return {
      ...headers,
      'Content-Type': 'application/json',
      ...(token && { Authorization: `Bearer ${token}` }),
    };
  }

  getRequestInit(body?: RequestInit['body'] | Record<string, unknown>): RequestInit {
    return {
      ...super.getRequestInit(body),
      credentials: 'include',
    };
  }

  searchToString(searchParams: Record<string, any>): string {
    return qs.stringify(searchParams, { arrayFormat: 'brackets' });
  }

  process(value: any, ...args: any[]) {
    return value?.data ?? value;
  }
}

async function getAuthToken(): Promise<string | null> {
  return localStorage.getItem('token');
}

セットアップ後の使用方法

ベースクラスを作成したら、RestEndpoint を直接使うのではなく、ベースクラスを使用します。

resource() と個別のエンドポイントの選択

API モジュールに単一のパスで標準的な CRUD 操作(一覧取得、取得、作成、更新、削除)がある場合は、resource() を使用します。こちらが一般的なケースです。

import { resource } from '@data-client/rest';
import { BaseEndpoint } from './BaseEndpoint';
import { Todo } from '../schemas/Todo';

export const TodoResource = resource({
  path: '/todos/:id',
  schema: Todo,
  Endpoint: BaseEndpoint,
});
// Provides: TodoResource.get, .getList, .create, .update, .delete, .partialUpdate

CRUD 以外の操作(検索、認証、カスタムアクション)や、パスが resource() の規約に一致しない場合は、単独の new BaseEndpoint() を使用します。

export const loginEndpoint = new BaseEndpoint({
  path: '/auth/login',
  method: 'POST' as const,
  body: {} as { email: string; password: string },
  schema: undefined,
});

ボディの型指定:undefined as unknown as BodyType ではなく、真値となる body: {} as BodyType を使用します。POST/PUT/PATCH でエンドポイントがリクエストボディを正しく送信するために、真値が必要です。

既存のバリデーションとの共存(Zod、Yup)

コードベースですでに Zod/Yup を使ってレスポンスを検証している場合は、キャッシュや正規化のメリットがある型について、Entity を信頼できる唯一の情報源として使うことを推奨します。正規化が不要な型(認証トークン、フォーム検証用の型、単発のレスポンス)に限り、Zod を維持します。詳しい選択肢については、移行用のリファレンスファイルを参照してください。

次のステップ

  1. Entity クラスを定義し(スキル「data-client-schema」)、schema: を介してエンドポイントに接続します — これは必須であり、省略できません。schema: undefined のエンドポイントは、正規化とキャッシュを迂回します。
  2. リソースとエンドポイントのパターンには、スキル「data-client-rest」を適用します
  3. フックベースの使用方法には、スキル「data-client-react」または「data-client-vue」を適用します

参考資料

コメント