TA

Manage data fetching and caching in Svelte apps.

Install

mkdir -p .claude/skills/tanstack-query && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/1100" && unzip -o skill.zip -d .claude/skills/tanstack-query && rm skill.zip

Installs to .claude/skills/tanstack-query

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use this skill when fetching data, managing server state, or handling API mutations in the Svelte frontend. Covers createQuery, createMutation, query keys, cache invalidation, optimistic updates, and WebSocket-driven refetching. Apply when adding API calls, managing loading/error states, or coordinating cache updates after mutations.
335 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Implement query keys for consistent cache management
  • Execute mutations with automatic cache invalidation
  • Perform optimistic UI updates for responsiveness
  • Sync state using WebSocket-driven invalidation
  • Manage dependent queries with enabled conditions

How it works

It centralizes API logic into feature-specific files, using query key factories to ensure cache consistency across queries and mutations.

Inputs & outputs

You give it
API endpoint and data requirements
You get back
Cached, reactive server state

When to use tanstack-query

  • Setup query keys and cache invalidation
  • Implement optimistic UI updates
  • Manage WebSocket-driven data synchronization

About this skill

TanStack Query

Documentation: tanstack.com/query. Use official docs when the local pattern is not enough.

Centralize API calls in api.svelte.ts per feature using TanStack Query with @exceptionless/fetchclient.

Query Basics

// 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;
        },
    }));
}

Query Keys Convention

Use a queryKeys factory per feature for type safety and consistency:

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,
};

Prefer the feature's queryKeys factory over ad-hoc arrays so WebSocket invalidation and cache updates share the same keys.

Mutations

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 });
        },
    }));
}

Naming Conventions

PatternNamingExample
Query (GET)get{Resource}QuerygetOrganizationsQuery()
Create (POST)post{Resource}MutationpostOrganizationMutation()
Update (PATCH)patch{Resource}MutationpatchOrganizationMutation()
Delete (DELETE)delete{Resource}MutationdeleteOrganizationMutation()

Dependent Queries

Use enabled to conditionally run queries: enabled: !!projectId.

Optimistic Updates

For mutations that update cached data optimistically: use onMutate to cancel in-flight queries, snapshot previous value via getQueryData, and apply optimistic update via setQueryData. Use onError to rollback from snapshot, and onSettled to always invalidateQueries for the final refetch.

WebSocket-Driven Invalidation

Invalidate queries when WebSocket messages arrive:

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 });
}

Wire WebSocket messages from the app layout to the feature invalidation helper.

When not to use it

  • When simple fetch calls are sufficient
  • When state is purely local and non-server-driven

Prerequisites

TanStack QuerySvelte

Limitations

  • Requires consistent use of query key factories

How it compares

It provides a structured, type-safe way to handle server state and cache synchronization compared to manual fetch management.

Compared to similar skills

tanstack-query side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
tanstack-query (this skill)72moNo flagsIntermediate
svelte-migrate27moNo flagsIntermediate
shopify-development126moReviewIntermediate
nuxt196moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry