data-client-rest について
data-client-restは、React/TypeScriptアプリで@data-client/restを使い、REST APIをモデル化して利用するためのコーディングスキルです。型付きスキーマ、正規化、キャッシュ管理に加え、リソースやエンドポイントの設計を扱います。
主な機能
- 01CRUDリソースとエンドポイントの定義
- 02データスキーマの宣言
- 03RESTデータの取得と更新
- 04楽観的更新の設定
- 05リソースエンドポイントの拡張
活用シーン
- 01型付きAPIデータモデルの作成
- 02リスト取得とページネーションの実装
- 03RESTデータ更新処理の作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ reactive/ data-client --skill data-client-restインストール後、Claude Code で/data-client-restを入力して読み込まれたか確認します。
詳細
リソースモデリングでの @data-client/rest の使用ガイド
このプロジェクトでは、 @data-client/rest を使用して、React/TypeScript アプリで RESTful リソースとエンティティを型安全性と自動キャッシュ管理のもとで定義、取得、正規化、更新します。
リモート API と連携するコードを生成するときは、必ず次のパターンに従ってください。
1. スキーマの定義
このプロジェクトでは、スキーマを使用して、型安全性と自動キャッシュ管理のもとでデータモデルを定義、正規化します。スキーマパターンには「data-client-schema」スキルを適用してください。 可変データ定義を生成するときは、必ず次のパターンに従ってください(「data-client-schema」スキルを適用してください)。
2. リソース(resource())
- resource() は、共通オブジェクトに対する CRUD 操作用の RestEndpoints のコレクションを作成します
- 必須フィールド:
path:path-to-regexp テンプレート(型付き)schema:単一の項目の宣言的なデータ形状(通常は Entity または Union)
- 任意:
urlPrefix:/以外の場合のホストルートsearchParams:MyResource.getList のクエリパラメーターの型(TS ジェネリック)paginationField:ページネーション用に MyResource.getList.getPage を追加しますoptimistic:ブール値。true の場合、すべてのミューテーションを楽観的に更新し、パフォーマンスを向上させますbody:MyResource.getList.push、MyResource.getList.unshift、MyResource.update、MyResource.partialUpdate の body パラメーターの型
import { Entity, resource } from '@data-client/rest';
import { User } from './User';
export class Todo extends Entity {
id = 0;
user = User.fromJS();
title = '';
completed = false;
createdAt = new Date();
static key = 'Todo';
static schema = {
user: User,
createdAt: (iso: string) => new Date(iso),
}
}
export const TodoResource = resource({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos/:id',
schema: Todo,
searchParams: {} as { userId?: string | number } | undefined,
paginationField: 'page',
nonFilterArgumentKeys: ['orderBy'],
optimistic: true,
});
使用方法
レンダリング
// GET https://jsonplaceholder.typicode.com/todos/5
const todo = useSuspense(TodoResource.get, { id: 5 });
// GET https://jsonplaceholder.typicode.com/todos
const todoList = useSuspense(TodoResource.getList);
// GET https://jsonplaceholder.typicode.com/todos?userId=1
const todoListByUser = useSuspense(TodoResource.getList, { userId: 1 });
ミューテーション
const ctrl = useController();
// PUT https://jsonplaceholder.typicode.com/todos/5
const updateTodo = todo => ctrl.fetch(TodoResource.update, { id }, todo);
// PATCH https://jsonplaceholder.typicode.com/todos/5
const partialUpdateTodo = todo =>
ctrl.fetch(TodoResource.partialUpdate, { id }, todo);
// POST https://jsonplaceholder.typicode.com/todos
const addTodoToStart = todo =>
ctrl.fetch(TodoResource.getList.unshift, todo);
// POST https://jsonplaceholder.typicode.com/todos?userId=1
const addTodoToEnd = todo => ctrl.fetch(TodoResource.getList.push, { userId: 1 }, todo);
// PATCH https://jsonplaceholder.typicode.com/todos/5
const toggleStatus = (completed: boolean) => ctrl.fetch(TodoResource.getList.move, { id }, { completed });
// DELETE https://jsonplaceholder.typicode.com/todos/5
const deleteTodo = id => ctrl.fetch(TodoResource.delete, { id });
// GET https://jsonplaceholder.typicode.com/todos?userId=1&page=2
const getNextPage = (page) => ctrl.fetch(TodoResource.getList.getPage, { userId: 1, page })
詳しい使用方法については、「data-client-react」または「data-client-vue」スキルを適用してください。
3. カスタム RestEndpoint パターン
/** Stand‑alone endpoint with custom typing */
export const getTicker = new RestEndpoint({
urlPrefix: 'https://api.exchange.coinbase.com',
path: '/products/:product_id/ticker',
schema: Ticker,
pollFrequency: 2000,
});
型指定のヒント
pathは第 1 引数用の path-to-regexp テンプレートですmethod≠GET⇒ 第 2 引数は body です(body: undefinedの場合を除く)searchParams/bodyの値は、型推論のためだけに指定しますRestEndpointを継承するときはRestGenericsを使用します
getOptimisticResponse()
getOptimisticResponse(snap, { id }) {
const article = snap.get(Article, { id });
if (!article) throw snap.abort;
return {
id,
votes: article.votes + 1,
};
}
RestEndpoint のライフサイクルメソッド
- フェッチの実行:
fetchResponse()→parseResponse()→process()- url(urlParams):
urlPrefix+path+(searchParams→searchToString()) - getRequestInit(body):
getHeaders()+method+signal
- url(urlParams):
4. リソースの拡張
.extend() を使用して、エンドポイントを追加またはオーバーライドします。
export const IssueResource = resource({
// ...base config...
}).extend((Base) => ({
search: Base.getList.extend({
path: '/search/issues',
// ...custom schema or params...
}),
}));
5. ベストプラクティスと注意事項
- ウェブアドレスを閲覧または移動するよう求められた場合は、実際にそのアドレスにアクセスしてください
- 正規化のため、すべてのリソース/エンティティ/コレクションに必ず
schemaを設定してください - 単一のエンドポイントを定義する場合や、ミューテーションエンドポイントが存在しない場合は、
resource()よりRestEndpointを優先してください - blob/ファイルのダウンロードや、その他の非 JSON レスポンスについては、network-transform: ファイルのダウンロードを参照してください。
6. 避けるべきよくある間違い
- ミューテーションエンドポイントを使用しない、または必要としない場合は、
resource()を使用しないでください
リファレンス
API の詳細なドキュメントについては、references ディレクトリを参照してください。
- resource - CRUD エンドポイントを作成します
- RestEndpoint;_EndpointLifecycle.md;RestEndpoint.js - 単一の REST エンドポイント
- Entity;_entity_lifecycle_methods.md - 正規化されたデータクラス
- Collection - 変更可能なリスト
- schema - スキーマの概要
- Fixtures - テスト用のモックデータ
- data-dependency;_useLive.md;_AsyncBoundary.md - レンダリングガイド
- mutations;_useLoading.md;_VoteDemo.md - ミューテーションガイド
ガイド(ユーザーがこれらのトピックについて尋ねた場合に参照してください):
- auth - 認証ヘッダー、トークン、ログイン/ログアウトのフロー
- pagination;_pagination.md - カーソル/オフセットページネーション、無限スクロール
- optimistic-updates;_optimisticTransform.md - サーバーの応答前に即座に UI へフィードバックを返します
- network-transform - 応答を変換し、標準に準拠しない API を処理します
概念(ユーザーがこれらのトピックについて尋ねた場合に参照してください):
- expiry-policy - キャッシュの無効化、古いデータ、dataExpiryLength、errorExpiryLength
- error-policy - エラー処理、再試行の動作、ソフトエラーとハードエラー
必ずこれらのパターンに従い、エッジケースについては公式ドキュメントを参照してください。スキーマ定義による自動正規化/キャッシュを活用した、慣用的で型安全なコードの生成を優先してください。