@dset/data
v1.0.0
Published
DSET data-access layer (Layer: data) — a facade over TanStack Query v5. Consumers use useResource/useMutationResource/createApiClient and never import TanStack directly.
Readme
@dset/data
The DSET data-access layer — a facade over TanStack Query v5 (ADR-0004).
Consumers use three primitives and never import @tanstack/react-query
directly, so the query engine stays a swappable implementation detail.
Layer rule: data may import only @dset/utils.
Primitives
| Export | Replaces in i3MS | Purpose |
| ------------------------------------ | -------------------------------------------- | ---------------------------------------------------------------------------------- |
| createApiClient(config) | services/apiClient.js + config/api.js | Typed fetch wrapper: JSON, error extraction, query strings, abort, raw responses |
| useResource(options) | useAdminResource(fetcher) | Read facade — { data, loading, error, refetch } + caching/dedup/polling |
| useMutationResource(options) | imperative service call + manual refetch() | Write facade with declarative invalidateKeys |
| DataProvider / createQueryClient | — | One-time setup with platform defaults |
Setup
import { DataProvider } from '@dset/data';
<DataProvider>
<App />
</DataProvider>;Reading data
import { useResource } from '@dset/data';
// The fetcher keeps i3MS's (signal) => Promise<T> shape exactly.
const { data, loading, error, refetch } = useResource({
key: ['invoices', { month }],
fetcher: (signal) => api.get('/invoices', { query: { month }, signal }),
});i3MS compatibility contract
The return shape is identical to useAdminResource — { data, loading, error,
refetch }, data is null until loaded, error is a normalized string. So
component code that consumes those fields migrates with zero changes. The only
delta is in the hook definition: it now takes an explicit, serializable key
(which TanStack requires, and which is exactly what unlocks caching, request
de-duplication and background refetch). The fetcher signature is unchanged.
Writing data
const { mutate, loading, error } = useMutationResource({
mutationFn: (gstin: string) => api.put('/transporter/gstin', { gstin }),
invalidateKeys: [['transporters']], // auto-refetch instead of manual refetch()
});Design notes
- TanStack is hidden. Only
queryClient.ts,DataProvider.tsxand the two hooks import it. Swapping the engine touches those files alone — no app changes. - Errors are normalized. Everything surfaces as an
ApiError(status + detail preserved); facades expose a plain stringerror. - Retry policy: never retry 4xx or aborts; retry transient/5xx up to twice.
staleTimestarts at 0 (parity with i3MS always-fresh fetches); raise it per-resource as caching is validated.
