UNPKG

wcz-layout

Version:

110 lines (95 loc) 4.03 kB
--- name: client-db description: "Use when: creating or modifying TanStack DB collections, live queries, optimistic collection writes, subset loading, collection preloading." --- > Mechanics (`createCollection`, `queryCollectionOptions`, the live-query builder, `createOptimisticAction`) are covered by TanStack's own skills load `@tanstack/db#db-core/live-queries`, `#db-core/mutations-optimistic`, and `@tanstack/react-db` for the API details. This skill adds only the wcz-layout conventions on top. ## Rules - Use `eager` syncMode for top-level collections, `on-demand` for child/relational data. - Always preload every `eager` collection used by a route, including collections used only by components rendered under that route. Do not preload `on-demand` collections. - Use `useLiveQuery` for list/grid pages and `useLiveSuspenseQuery` for detail components wrapped in suspense. - Shape data in the collection query whenever possible: filter, order, group and aggregate only the fields the consumer needs. Do not load broad results and further filter or transform them in component code. - For mutations use `createOptimisticAction` mirror the server mutation in `onMutate`, then call the server function and `collection.utils.refetch()` in `mutationFn`. - For on-demand subset loading, convert `meta.loadSubsetOptions` with `fromTanDb()` (from `agnostic-query/tanstack-db`) before passing it to the server fn, and set `autoIndex: "eager"` with `defaultIndexType: BasicIndex` (from `@tanstack/react-db`). ## File Placement ``` src/db-collections/ DB collections src/server/actions/ server functions (select/update/insert/delete) src/lib/schemas/ Zod schemas (shared between client and server) src/lib/auth/scopes.ts API scope keys ``` ## Examples ```ts // src/db-collections/<feature>.ts export const librariesCollection = createCollection( queryCollectionOptions({ queryKey: ["libraries"], queryFn: () => selectLibraries(), getKey: ({ id }) => id, schema: LibrarySchema, queryClient: queryClient, syncMode: "eager", }), ); // src/db-collections/<feature>.ts child/relational (on-demand) import { BasicIndex } from "@tanstack/react-db"; import { fromTanDb } from "agnostic-query/tanstack-db"; export const bookCollection = createCollection( queryCollectionOptions({ queryKey: ["books"], queryFn: ({ meta }) => selectBooks({ data: fromTanDb(meta?.loadSubsetOptions) }), getKey: ({ id }) => id, schema: BookSchema, queryClient: queryClient, syncMode: "on-demand", autoIndex: "eager", defaultIndexType: BasicIndex, }), ); // Preload export const Route = createFileRoute("/")({ loader: () => { void librariesCollection.preload(); }, }); // simple query const { data, isLoading } = useLiveQuery((q) => q.from({ library: libraryCollection }).orderBy(({ library }) => library.name, "asc"), ); // advanced query with relational data const { data } = useLiveQuery((q) => q .from({ library: libraryCollection }) .where(({ library }) => eq(library.id, id)) .findOne() .select(({ library }) => ({ ...library, books: toArray( q .from({ book: bookCollection }) .where(({ book }) => eq(book.libraryId, library.id)) .orderBy(({ book }) => book.title, "asc") .select(({ book }) => ({ id: book.id, title: book.title, })), ), })), ); // form submission handler const handleOnSubmit = createOptimisticAction<Library>({ onMutate: (formValues) => { libraryCollection.update(id, (prev) => Object.assign(prev, formValues)); }, mutationFn: async (formValues) => { await updateLibrary({ data: formValues }); await libraryCollection.utils.refetch(); }, }); // calling the handler try { const transaction = handleOnSubmit(formValues); await transaction.isPersisted.promise; } catch (error) { if (error instanceof Error) alert(error.message); } ```