From ce6b79ec95b6ac17b2168de9199597a64fb8ae3c Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 23 Apr 2026 18:17:34 -0400 Subject: [PATCH] feat(web): add TanStack Query helpers for library endpoints qk.{artists,artist,album} key generators and create*Query wrappers with shared page size, sort-aware keys, and infinite-query plumbing for /api/artists. --- web/src/lib/api/queries.test.ts | 17 ++++++++++++++ web/src/lib/api/queries.ts | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 web/src/lib/api/queries.test.ts create mode 100644 web/src/lib/api/queries.ts diff --git a/web/src/lib/api/queries.test.ts b/web/src/lib/api/queries.test.ts new file mode 100644 index 00000000..d100ef5b --- /dev/null +++ b/web/src/lib/api/queries.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest'; +import { qk } from './queries'; + +describe('qk key generators', () => { + test('artists key includes sort discriminator', () => { + expect(qk.artists('alpha')).toEqual(['artists', { sort: 'alpha' }]); + expect(qk.artists('newest')).toEqual(['artists', { sort: 'newest' }]); + }); + + test('artist key tuple', () => { + expect(qk.artist('abc')).toEqual(['artist', 'abc']); + }); + + test('album key tuple', () => { + expect(qk.album('xyz')).toEqual(['album', 'xyz']); + }); +}); diff --git a/web/src/lib/api/queries.ts b/web/src/lib/api/queries.ts new file mode 100644 index 00000000..6d6148d0 --- /dev/null +++ b/web/src/lib/api/queries.ts @@ -0,0 +1,41 @@ +import { createQuery, createInfiniteQuery } from '@tanstack/svelte-query'; +import { api } from './client'; +import type { ArtistRef, ArtistDetail, AlbumDetail, Page } from './types'; + +export type ArtistSort = 'alpha' | 'newest'; +export const ARTIST_PAGE_SIZE = 50; + +export const qk = { + artists: (sort: ArtistSort) => ['artists', { sort }] as const, + artist: (id: string) => ['artist', id] as const, + album: (id: string) => ['album', id] as const, +}; + +export function createArtistsQuery(sort: ArtistSort) { + return createInfiniteQuery({ + queryKey: qk.artists(sort), + queryFn: ({ pageParam = 0 }) => + api.get>( + `/api/artists?sort=${sort}&limit=${ARTIST_PAGE_SIZE}&offset=${pageParam}` + ), + initialPageParam: 0, + getNextPageParam: (last) => { + const loaded = last.offset + last.items.length; + return loaded >= last.total ? undefined : loaded; + }, + }); +} + +export function createArtistQuery(id: string) { + return createQuery({ + queryKey: qk.artist(id), + queryFn: () => api.get(`/api/artists/${id}`), + }); +} + +export function createAlbumQuery(id: string) { + return createQuery({ + queryKey: qk.album(id), + queryFn: () => api.get(`/api/albums/${id}`), + }); +}