Appearance
TanStack Query (Vue Query) ​
Overview ​
The Brand Portal uses TanStack Vue Query (v5) for server state management: caching, request deduplication, background refetching, and mutations with cache invalidation. All Query-related composables and utilities live in app/composables/api/ alongside the imperative use*Api composables.
When to Use Queries vs API Composables ​
| Use case | Use | Reason |
|---|---|---|
| Cached, reactive data (dashboard, folders, profile, sharing list) | use*Queries composables | Caching, deduplication, loading/error state, refetch on focus |
| One-off or imperative calls (login, download, submit form) | use*Api composables | No cache needed; direct await |
| Cache invalidation after a mutation | useQueryClient() + queryClient.invalidateQueries({ queryKey }) | Keep UI in sync |
| Custom query/mutation with shared error handling | useQuery/useMutation + queryKeys + createQueryFn/createMutationFn | Consistency with global plugin |
Plugin Configuration ​
The Vue Query plugin configures the global QueryClient:
- staleTime: From
NUXT_PUBLIC_QUERY_STALE_TIME(default 5 minutes). Used bygetDefaultStaleTime()inuseQueryClient. - Global error handling: 401 triggers logout; other errors show snackbar via
getApiErrorMessagefrom~/utils/error. - SSR: Hydrate/dehydrate so server-fetched data is available on the client without refetch.
queryKeys ​
Path: app/composables/api/queryKeys.ts
Centralized query key factory for type-safe cache invalidation. All keys should be defined here.
Structure:
- dashboard:
all,data(workspaceId, instanceId) - tiles:
all,list(workspaceId) - banners:
all,list(workspaceId, instanceId) - folders:
all,list(workspaceId, subfolderList) - brand:
all,details(brandName) - auth:
all,user,session - account:
all,status(workspaceId) - assets:
all,details(assetId, workspaceId) - folderDetails:
all,details(params),count(folderId, workspaceId) - collections:
all,list(workspaceId),details(collectionId, params) - sharing:
all,list(params)
Usage:
typescript
import { queryKeys } from '~/composables/api/queryKeys'
queryClient.invalidateQueries({ queryKey: queryKeys.dashboard.all })
queryClient.getQueryData(queryKeys.folderDetails.details(params))useQueryClient ​
Path: app/composables/api/useQueryClient.ts
Provides access to the Vue Query QueryClient and helpers:
- getDefaultStaleTime(overrideMs?): Returns
NUXT_PUBLIC_QUERY_STALE_TIMEor 5 minutes (for use instaleTimeoption). - createQueryFn(apiCall): Wraps an API call for use as
queryFn; errors are rethrown (handled by plugin). - createQueryFnWithParams(apiCall): Same for parameterized API calls.
- serializeQueryParams(params): Serializes params for stable query keys (used by
queryKeys).
Use useQueryClient() from @tanstack/vue-query to get the QueryClient instance for invalidateQueries, setQueryData, etc.
useMutationClient ​
Path: app/composables/api/useMutationClient.ts
Utilities for mutations:
- createMutationFn(apiCall): Wraps an API call for use as
mutationFn; errors rethrown (handled by plugin). - invalidateQueries(queryClient, queryKeys): Invalidates given query keys after a successful mutation.
- Helpers for optimistic updates with rollback (see source).
Usage:
typescript
import { useMutation } from '@tanstack/vue-query'
import { createMutationFn, invalidateQueries } from '~/composables/api/useMutationClient'
import { queryKeys } from '~/composables/api/queryKeys'
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: createMutationFn((data) => sharingApi.createLink(data)),
onSuccess: () => {
invalidateQueries(queryClient, queryKeys.sharing.all)
},
})use*Queries Composables ​
Each composable wraps one or more useQuery/useMutation calls and exposes reactive data and methods. They use queryKeys and the same API layer as use*Api so cache is shared (e.g. folder details page and sidebar tree).
| Composable | Purpose |
|---|---|
| useAppDataQueries | Dashboard data, tiles, banners; workspace-scoped. |
| useAssetsQueries | Asset details; cache by assetId and workspaceId. |
| useCollectionQueries | Collections list and details; cache by workspaceId and collectionId. |
| useFolderQueries | Root folders list; cache by workspaceId. |
| useProfileQueries | User profile (e.g. auth user). |
| useSharingQueries | Sharing list; cache by params. |
Example:
typescript
const { dashboardQuery } = useAppDataQueries(workspaceId, instanceId)
// dashboardQuery.data, dashboardQuery.isLoading, dashboardQuery.refetchRelated ​
- Plugins - Vue Query - QueryClient setup and global error handling
- Utils - error - getApiErrorMessage used by plugin
- Composable APIs - Index - use*Api composables