## Overview Complete migration from service layer to TanStack Query v6 with mutations, queries, and automatic cache management. ## What Was Completed ### Phase 1: Entity Queries & Database Pages ✅ - Created `entity.queries.ts` with query options for weapons, characters, and summons - Migrated all database detail pages to use TanStack Query with `withInitialData()` pattern - SSR with client-side hydration working correctly ### Phase 2: Server Load Cleanup ✅ - Removed PartyService dependency from teams detail server load - Server loads now use adapters directly instead of service layer - Cleaner separation of concerns ### Phase 3: Party.svelte Refactoring ✅ - Removed all PartyService, GridService, and ConflictService dependencies - Migrated to TanStack Query mutations for all operations: - Grid operations: create, update, delete, swap, move - Party operations: update, delete, remix, favorite, unfavorite - Added swap/move mutations for drag-and-drop operations - Automatic cache invalidation and query refetching ### Phase 4: Service Layer Removal ✅ - Deleted all service layer files (~1,345 lines removed): - `party.service.ts` (620 lines) - `grid.service.ts` (450 lines) - `conflict.service.ts` (120 lines) - `gridOperations.ts` (unused utility) - Deleted empty `services/` directory - Created utility functions for cross-cutting concerns: - `localId.ts`: Anonymous user local ID management - `editKeys.ts`: Edit key management for anonymous editing - `party-context.ts`: Extracted PartyContext type ### Phase 5: /teams/new Migration ✅ - Migrated party creation wizard to use TanStack Query mutations - Replaced all direct adapter calls with mutations (7 locations) - Maintains existing behavior and flow - Automatic cache invalidation for newly created parties ## Benefits ### Performance - Automatic request deduplication - Better cache utilization across pages - Background refetching for fresh data - Optimistic updates for instant UI feedback ### Developer Experience - Single source of truth for data fetching - Consistent patterns across entire app - Query devtools for debugging - Less boilerplate code ### Code Quality - ~1,088 net lines removed - Simpler mental model (no service layer) - Better TypeScript inference - Easier to test ### Architecture - **100% TanStack Query coverage** - no service layer, no direct adapter calls - Clear separation: UI ← Queries/Mutations ← Adapters ← API - Automatic cache management - Consistent mutation patterns everywhere ## Testing All features verified: - Party creation (anonymous & authenticated) - Grid operations (add, remove, update, swap, move) - Party operations (update, delete, remix, favorite) - Cache invalidation across tabs - Error handling and rollback - SSR with hydration ## Files Modified ### Created (3) - `src/lib/types/party-context.ts` - `src/lib/utils/editKeys.ts` - `src/lib/utils/localId.ts` ### Deleted (4) - `src/lib/services/party.service.ts` - `src/lib/services/grid.service.ts` - `src/lib/services/conflict.service.ts` - `src/lib/utils/gridOperations.ts` ### Modified (13) - `src/lib/api/mutations/grid.mutations.ts` - `src/lib/components/grids/CharacterGrid.svelte` - `src/lib/components/grids/SummonGrid.svelte` - `src/lib/components/grids/WeaponGrid.svelte` - `src/lib/components/party/Party.svelte` - `src/routes/teams/new/+page.svelte` - Database entity pages (characters, weapons, summons) - Other supporting files ## Migration Complete This PR completes the TanStack Query migration. The entire application now uses TanStack Query v6 for all data fetching and mutations, with zero remaining service layer code. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| cacheHelpers.ts | ||
| keys.ts | ||
| MIGRATION.md | ||
| queryClient.ts | ||
| README.md | ||
| ssr.ts | ||
TanStack Query SSR Integration
This directory contains utilities for integrating TanStack Query v6 with SvelteKit's server-side rendering.
Architecture Overview
The project uses a hybrid approach for SSR:
-
QueryClient in Layout: The
QueryClientis created in+layout.tsand passed to+layout.sveltevia the load function. This enables prefetching in child page load functions. -
Server Data with initialData: Pages that use
+page.server.tscan pass server-fetched data asinitialDatato TanStack Query using thewithInitialData()helper. -
Prefetching in +page.ts: Pages that use
+page.ts(universal load functions) can useprefetchQuery()to populate the QueryClient cache before rendering.
Usage Examples
Pattern 1: Using Server Data with initialData
For pages that already fetch data in +page.server.ts:
<!-- +page.svelte -->
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'
import { partyQueries } from '$lib/api/queries/party.queries'
import { withInitialData } from '$lib/query/ssr'
import type { PageData } from './$types'
let { data } = $props<{ data: PageData }>()
// Use server-fetched party as initial data
// The query won't refetch until the data becomes stale
const party = createQuery(() => ({
...partyQueries.byShortcode(data.party?.shortcode ?? ''),
...withInitialData(data.party),
enabled: !!data.party?.shortcode
}))
</script>
{#if $party.data}
<h1>{$party.data.name}</h1>
{/if}
Pattern 2: Prefetching in Universal Load Functions
For pages that can use +page.ts (not server-only):
// +page.ts
import type { PageLoad } from './$types'
import { prefetchQuery } from '$lib/query/ssr'
import { partyQueries } from '$lib/api/queries/party.queries'
export const load: PageLoad = async ({ parent, params }) => {
const { queryClient } = await parent()
// Prefetch party data into the cache
await prefetchQuery(queryClient, partyQueries.byShortcode(params.id))
// No need to return data - it's already in the QueryClient cache
return { shortcode: params.id }
}
<!-- +page.svelte -->
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'
import { partyQueries } from '$lib/api/queries/party.queries'
import type { PageData } from './$types'
let { data } = $props<{ data: PageData }>()
// Data is already in cache from prefetch - no loading state on initial render
const party = createQuery(() => partyQueries.byShortcode(data.shortcode))
</script>
{#if $party.data}
<h1>{$party.data.name}</h1>
{/if}
Pattern 3: Infinite Queries with Prefetching
For paginated data:
// +page.ts
import type { PageLoad } from './$types'
import { prefetchInfiniteQuery } from '$lib/query/ssr'
import { partyQueries } from '$lib/api/queries/party.queries'
export const load: PageLoad = async ({ parent }) => {
const { queryClient } = await parent()
// Prefetch first page of parties
await prefetchInfiniteQuery(queryClient, partyQueries.list())
}
Migration Guide
From Server-Only to TanStack Query
- Keep existing +page.server.ts - No changes needed to server load functions
- Add TanStack Query to component - Use
createQuerywithwithInitialData - Benefit from caching - Subsequent navigations use cached data
From Custom Resources to TanStack Query
- Replace resource imports with query/mutation imports
- Use createQuery instead of resource state
- Use mutations for CRUD operations with automatic cache invalidation
Files
queryClient.ts- QueryClient factory (legacy, kept for reference)ssr.ts- SSR utilities (withInitialData, prefetchQuery, etc.)