Severity: Medium.From a frontend deep audit, 2026-06-08. Structural cleanup of the data-access module.
Problems
web/src/api/queries.ts is 584 lines spanning 8 domains (error classes, auth, objects, fields-on-object, field-definitions, vocab, terms, authorities, search). No hook crosses a domain boundary, so the split is low-risk.
query-client.ts:9 imports HttpError/InUseError from queries.ts, dragging the whole hook module into the toast-wiring path.
No query-key factory — every key is a hand-written literal (["objects", params], ["object", id], ["terms", vocabularyId], …) across the module + config-provider.tsx:10. Collision-safe today but typo-prone.
Object writes don't invalidate ["search"].useUpdateObject/useDeleteObject/useSetVisibility (queries.ts:205-208,250,413-416) invalidate ["objects"]/["object", id] but never the search index, so the search panel serves stale hits until refetch. Needs a deliberate decision (invalidate vs accept Meilisearch lag).
Move error classes out first (decouples query-client.ts). Extract repeated boilerplate: unwrap(result, msg) for the if (error || !data) throw GET preamble (6×), and the 409 → InUseError block (4×). Then make an explicit call on the ["search"] invalidation gap (and document it).
Source: frontend deep audit (data-layer dimension), 2026-06-08. Note: the staleTime split (reference data 5min vs objects none) and the missing keepPreviousData on client-side-filtered reference lists are both intentional/correct — not part of this.
**Severity: Medium.** _From a frontend deep audit, 2026-06-08. Structural cleanup of the data-access module._
## Problems
- **`web/src/api/queries.ts` is 584 lines spanning 8 domains** (error classes, auth, objects, fields-on-object, field-definitions, vocab, terms, authorities, search). No hook crosses a domain boundary, so the split is low-risk.
- **`query-client.ts:9` imports `HttpError`/`InUseError` from `queries.ts`**, dragging the whole hook module into the toast-wiring path.
- **No query-key factory** — every key is a hand-written literal (`["objects", params]`, `["object", id]`, `["terms", vocabularyId]`, …) across the module + `config-provider.tsx:10`. Collision-safe today but typo-prone.
- **Object writes don't invalidate `["search"]`.** `useUpdateObject`/`useDeleteObject`/`useSetVisibility` (`queries.ts:205-208,250,413-416`) invalidate `["objects"]`/`["object", id]` but never the search index, so the search panel serves stale hits until refetch. Needs a deliberate decision (invalidate vs accept Meilisearch lag).
## Suggested fix
Proposed layout:
```
src/api/
client.ts (unchanged)
errors.ts HttpError, FieldRejection, InUseError, VisibilityError
query-keys.ts typed key factory: keys.objects(p?), keys.object(id), keys.terms(vocabId), …
queries/
auth.ts useMe, useLogin, useLogout
objects.ts useObjectsPage, useObject, useCreate/Update/DeleteObject, useSetFields, useSetVisibility
field-defs.ts useFieldDefinitions, useCreate/Update/DeleteFieldDefinition
vocab.ts useVocabularies, useCreate/Rename/DeleteVocabulary, useTerms, useAddTerm, useUpdate/DeleteTerm
authorities.ts useAuthorities, useCreate/Update/DeleteAuthority
search.ts useSearch, SEARCH_PAGE
```
Move error classes out first (decouples `query-client.ts`). Extract repeated boilerplate: `unwrap(result, msg)` for the `if (error || !data) throw` GET preamble (6×), and the `409 → InUseError` block (4×). Then make an explicit call on the `["search"]` invalidation gap (and document it).
_Source: frontend deep audit (data-layer dimension), 2026-06-08. Note: the staleTime split (reference data 5min vs objects none) and the missing `keepPreviousData` on client-side-filtered reference lists are both intentional/correct — not part of this._
api/errors.ts now owns the 4 error classes; error-message.ts imports them from ./errors, so the toast path (query-client → error-message → errors) no longer transitively loads the react-query hook module. The barrel re-exports the classes, so all existing importers are unchanged.
api/queries.ts (584 lines) → api/queries/{auth,objects,field-defs,vocab,authorities,search}.ts + index.ts barrel. All 27 hooks moved byte-for-byte; api/queries.ts deleted. The ../api/queries import path is unchanged for all ~30 consumers (resolves to the directory index) — zero consumer churn.
api/query-keys.ts central keys factory used by every queryKey/invalidate/setQueryData site, including config-provider's ["config"].
Search invalidation:useUpdateObject/useDeleteObject/useSetVisibility now invalidate ["search"] (no-op when not searching since the search query is term-gated) — the one intentional behavior change, with a focused test.
265 tests pass (existing suites unchanged); typecheck/lint/build clean; check:size 216.5 KB gz (≈unchanged); check:colors clean; no new dependency; no new i18n keys; no codename.
Deferred (cosmetic): repointing the 4 component error-importers from the ../api/queries re-export to ../api/errors directly.
Done in merge `002af9d`.
- **`api/errors.ts`** now owns the 4 error classes; **`error-message.ts` imports them from `./errors`**, so the toast path (`query-client → error-message → errors`) no longer transitively loads the react-query hook module. The barrel re-exports the classes, so all existing importers are unchanged.
- **`api/queries.ts` (584 lines) → `api/queries/{auth,objects,field-defs,vocab,authorities,search}.ts` + `index.ts`** barrel. All 27 hooks moved byte-for-byte; `api/queries.ts` deleted. The `../api/queries` import path is unchanged for all ~30 consumers (resolves to the directory index) — zero consumer churn.
- **`api/query-keys.ts`** central `keys` factory used by every queryKey/invalidate/setQueryData site, including `config-provider`'s `["config"]`.
- **Search invalidation:** `useUpdateObject`/`useDeleteObject`/`useSetVisibility` now invalidate `["search"]` (no-op when not searching since the search query is term-gated) — the one intentional behavior change, with a focused test.
265 tests pass (existing suites unchanged); typecheck/lint/build clean; check:size 216.5 KB gz (≈unchanged); check:colors clean; no new dependency; no new i18n keys; no codename.
**Deferred (cosmetic):** repointing the 4 component error-importers from the `../api/queries` re-export to `../api/errors` directly.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Severity: Medium. From a frontend deep audit, 2026-06-08. Structural cleanup of the data-access module.
Problems
web/src/api/queries.tsis 584 lines spanning 8 domains (error classes, auth, objects, fields-on-object, field-definitions, vocab, terms, authorities, search). No hook crosses a domain boundary, so the split is low-risk.query-client.ts:9importsHttpError/InUseErrorfromqueries.ts, dragging the whole hook module into the toast-wiring path.["objects", params],["object", id],["terms", vocabularyId], …) across the module +config-provider.tsx:10. Collision-safe today but typo-prone.["search"].useUpdateObject/useDeleteObject/useSetVisibility(queries.ts:205-208,250,413-416) invalidate["objects"]/["object", id]but never the search index, so the search panel serves stale hits until refetch. Needs a deliberate decision (invalidate vs accept Meilisearch lag).Suggested fix
Proposed layout:
Move error classes out first (decouples
query-client.ts). Extract repeated boilerplate:unwrap(result, msg)for theif (error || !data) throwGET preamble (6×), and the409 → InUseErrorblock (4×). Then make an explicit call on the["search"]invalidation gap (and document it).Source: frontend deep audit (data-layer dimension), 2026-06-08. Note: the staleTime split (reference data 5min vs objects none) and the missing
keepPreviousDataon client-side-filtered reference lists are both intentional/correct — not part of this.Done in merge
002af9d.api/errors.tsnow owns the 4 error classes;error-message.tsimports them from./errors, so the toast path (query-client → error-message → errors) no longer transitively loads the react-query hook module. The barrel re-exports the classes, so all existing importers are unchanged.api/queries.ts(584 lines) →api/queries/{auth,objects,field-defs,vocab,authorities,search}.ts+index.tsbarrel. All 27 hooks moved byte-for-byte;api/queries.tsdeleted. The../api/queriesimport path is unchanged for all ~30 consumers (resolves to the directory index) — zero consumer churn.api/query-keys.tscentralkeysfactory used by every queryKey/invalidate/setQueryData site, includingconfig-provider's["config"].useUpdateObject/useDeleteObject/useSetVisibilitynow invalidate["search"](no-op when not searching since the search query is term-gated) — the one intentional behavior change, with a focused test.265 tests pass (existing suites unchanged); typecheck/lint/build clean; check:size 216.5 KB gz (≈unchanged); check:colors clean; no new dependency; no new i18n keys; no codename.
Deferred (cosmetic): repointing the 4 component error-importers from the
../api/queriesre-export to../api/errorsdirectly.