Install
$ agentstack add skill-deadlymind-nanolama-react-query ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
React Query (server state with TanStack Query v5)
When to use
Any component that reads or writes data owned by the Django/DRF backend. Server state is asynchronous, shared, and can go stale under you — that is Query's job. Do not copy fetched rows into Zustand or useState; mirror-storing server data is the top source of stale-UI bugs on this stack.
Pattern
Three rules:
- Query keys are structured arrays from a factory, never ad-hoc strings.
A key is ['tenant', tenantId, 'invoices', 'list', filters] — a stable, serializable description of what the data is, which under a per-tenant API includes whose it is. Prefix-matching then powers targeted invalidation.
- **
staleTimesets how long data is trusted;gcTimehow long an unused
cache lingers.** Pick per resource — not the defaults everywhere.
- After a mutation, invalidate by key prefix in
onSuccess. Don't manually
patch the cache for the common case; let the refetch reconcile with the server.
Centralize keys in a factory, read with useQuery, write with useMutation, then invalidate by prefix in onSuccess:
// features/invoices/keys.ts — tenant is part of the key, not just a header
export const invoiceKeys = {
all: (t: string) => ['tenant', t, 'invoices'] as const,
lists: (t: string) => [...invoiceKeys.all(t), 'list'] as const,
list: (t: string, filters: InvoiceFilters) => [...invoiceKeys.lists(t), filters] as const,
detail: (t: string, id: number) => [...invoiceKeys.all(t), 'detail', id] as const,
};
// Read (v5 single-object signature; no-data status is `isPending`)
const tenantId = useUI((s) => s.activeEntrepriseId); // see `zustand-state`
const { data, isPending, isError } = useQuery({
queryKey: invoiceKeys.list(tenantId, filters),
queryFn: () => fetchInvoices(filters), // returns a Zod-parsed payload
enabled: !!tenantId,
staleTime: 30_000, // 30s: trust the list before refetching
gcTime: 5 * 60_000, // keep unused cache 5min for fast back-nav
});
// Write, then prefix-invalidate so every matching list/detail refetches
const qc = useQueryClient();
const { mutate } = useMutation({
mutationFn: (body: InvoiceInput) => createInvoice(body),
onSuccess: () => qc.invalidateQueries({ queryKey: invoiceKeys.all(tenantId) }),
});
// mutate(values) — call from the form's submit handler
The server-state vs client-state boundary
| Belongs in React Query | Belongs in Zustand / local state | | --- | --- | | API rows, lists, details | Sidebar open, active tab, theme | | Anything the server owns | Draft form input before submit | | Derived-from-fetched values | Selected row id / UI filters* |
*Filters live in client state, then feed into the query key — the key is the one place client and server state meet. Never duplicate the fetched rows.
Switching tenants
A tenant switch changes which rows every key means, so it runs as an ordered sequence — set the store, cancelQueries, remove the old tenant's entries, resubscribe sockets, let observers refetch. The order is the safety property; see tenant-session-switch for the full procedure and tenant-aware persistence.
Adapt to your repo
Rename invoices/invoiceKeys and the accessor (/api/invoices/) per resource, and keep one keys file per feature. Tune staleTime to how fast the data changes (near-real-time dashboards → low or 0; reference lists → minutes). Wrap the app once in a QueryClientProvider in a client boundary under the App Router. Make queryFn return the Zod-validated shape (see drf-zod-contract).
Gotchas
- v5 renamed
isLoading→isPendingfor the no-data state,cacheTime→gcTime,
and takes a single object — positional useQuery(key, fn) no longer exists.
invalidateQueriesprefix-matches, so{ queryKey: ['invoices'] }catches
every list and detail; scoping too narrowly leaves stale sibling views.
staleTime: 0(the default) refetches on every mount/focus — deliberate, but
chatty for stable data. Set it up rather than leaving it implicit.
- Reading
dataoff a mirrored Zustand copy defeats invalidation — the refetch
updates the cache, not your snapshot. Read straight from useQuery.
- Cookie-JWT auth means the browser sends the HttpOnly cookie automatically, but
credentials: 'include' alone only covers reads — mutating mutationFn calls also need the X-CSRFToken header. Go through the shared client (see nextjs-module) rather than hand-rolling fetch in a queryFn.
- **Security-critical: the tenant belongs IN the query key, not only in the request
header.** Two tenants otherwise share the cache entry ['invoices','list',filters], and after a tenant switch the cache happily serves the previous tenant's rows — a cross-tenant leak your backend never sees. Scope every key under ['tenant', tenantId, ...] so two tenants can never collide in one entry (the purge on switch is tenant-session-switch's job). Client-side isolation is defence-in-depth, not the boundary — the server still enforces it (see multi-tenancy, rbac-permissions).
See also
nextjs-moduletenant-session-switchzustand-statedrf-zod-contract
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Deadlymind
- Source: Deadlymind/nanolama
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.