mcpField
T
tanstack-query

tanstack-query

github.com/exceptionless開発2.5k53 回インストール3 日前に更新
リポジトリ

tanstack-query について

Svelteアプリ開発者向けのTanStack Queryによるデータ取得の指針です。API呼び出しの集約、クエリキーの規約、ミューテーション、WebSocket連動のキャッシュ無効化を扱います。

主な機能

  1. 01機能ごとのAPI呼び出し集約
  2. 02クエリキーのファクトリー化
  3. 03クエリとミューテーションの命名規約
  4. 04楽観的更新とロールバック
  5. 05WebSocketメッセージによるキャッシュ無効化

活用シーン

  1. 01機能ごとのデータリクエスト整理
  2. 02クエリキーとキャッシュ更新の統一
  3. 03ロールバック付き楽観的ミューテーションの実装
  4. 04WebSocketメッセージに応じたクエリ更新

インストール

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

$npx skills add https://github.com/exceptionless/exceptionless --skill tanstack-query

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

詳細

TanStack Query

ドキュメント: tanstack.com/query。ローカルのパターンだけでは不十分な場合は、公式ドキュメントを参照してください。

@exceptionless/fetchclient と TanStack Query を使用して、機能ごとに api.svelte.ts に API 呼び出しをまとめます。

クエリの基本

// src/lib/features/organizations/api.svelte.ts
import { createQuery, createMutation, useQueryClient } from "@tanstack/svelte-query";
import { type FetchClientResponse, type ProblemDetails, useFetchClient } from "@exceptionless/fetchclient";
import { accessToken } from "$features/auth/index.svelte";

const queryKeys = {
    type: ["Organization"] as const,
};

export function getOrganizationsQuery() {
    return createQuery<FetchClientResponse<Organization[]>, ProblemDetails>(() => ({
        enabled: () => !!accessToken.current,
        queryKey: queryKeys.type,
        queryFn: async ({ signal }: { signal: AbortSignal }) => {
            const client = useFetchClient();
            const response = await client.getJSON<Organization[]>("/organizations", { signal });
            return response;
        },
    }));
}

クエリキーの規約

型安全性と一貫性を確保するため、機能ごとに queryKeys ファクトリーを使用します。

export const queryKeys = {
    type: ["Webhook"] as const,
    id: (id: string | undefined) => [...queryKeys.type, id] as const,
    ids: (ids: string[] | undefined) => [...queryKeys.type, ...(ids ?? [])] as const,
    project: (id: string | undefined) => [...queryKeys.type, "project", id] as const,
    deleteWebhook: (ids: string[] | undefined) => [...queryKeys.ids(ids), "delete"] as const,
    postWebhook: () => [...queryKeys.type, "post"] as const,
};

WebSocket による無効化とキャッシュ更新で同じキーを共有できるよう、場当たり的な配列ではなく、その機能の queryKeys ファクトリーを優先して使用してください。

ミューテーション

export function postOrganizationMutation() {
    const queryClient = useQueryClient();

    return createMutation(() => ({
        mutationFn: async (data: CreateOrganizationRequest) => {
            const client = useFetchClient();
            const response = await client.postJSON<Organization>("/organizations", data);
            return response.data!;
        },
        onSuccess: () => {
            queryClient.invalidateQueries({ queryKey: queryKeys.type });
        },
    }));
}

命名規則

パターン命名例
クエリ(GET)get{Resource}QuerygetOrganizationsQuery()
作成(POST)post{Resource}MutationpostOrganizationMutation()
更新(PATCH)patch{Resource}MutationpatchOrganizationMutation()
削除(DELETE)delete{Resource}MutationdeleteOrganizationMutation()

依存するクエリ

クエリを条件付きで実行するには、enabled を使用します。例:enabled: !!projectId。

楽観的更新

キャッシュされたデータを楽観的に更新するミューテーションでは、onMutate を使用して実行中のクエリをキャンセルし、getQueryData で以前の値のスナップショットを取得してから、setQueryData で楽観的な更新を適用します。onError でスナップショットからロールバックし、onSettled で必ず invalidateQueries を呼び出して最終的に再取得します。

WebSocket による無効化

WebSocket メッセージの受信時にクエリを無効化します。

export async function invalidateWebhookQueries(
    queryClient: QueryClient,
    message: WebSocketMessageValue<"WebhookChanged">,
) {
    const { id, organization_id, project_id } = message;

    if (id) await queryClient.invalidateQueries({ queryKey: queryKeys.id(id) });
    if (project_id) await queryClient.invalidateQueries({ queryKey: queryKeys.project(project_id) });
    if (!id && !organization_id && !project_id)
        await queryClient.invalidateQueries({ queryKey: queryKeys.type });
}

アプリのレイアウトから WebSocket メッセージを機能の無効化ヘルパーに接続します。

コメント