Skip to main content
Status: Accepted Date: 2026-02-06 Deciders: Reflections Maintainers

Context

The web app needs predictable data fetching/caching, lightweight local UI state, and realtime awareness of ingestion/pipeline updates without over-complicating architecture.

Decision

Adopt a layered frontend state model:
  • Next.js App Router for route/layout composition.
  • TanStack React Query for server-state fetching, caching, and invalidation.
  • Zustand for UI/session-local state slices.
  • Supabase Realtime broadcast subscription for pipeline status updates, mapped to targeted React Query invalidations.

Alternatives considered

Alternative 1: React context + custom fetch cache only

Pros:
  • Fewer external dependencies.
Cons:
  • Reinvents stale/cache/invalidation semantics.
  • More bespoke code and consistency risk.

Alternative 2: Global Redux-only state for everything

Pros:
  • Single state container.
Cons:
  • Higher boilerplate for server-state workflows.
  • Less natural fit for request lifecycle caching.

Alternative 3: Polling-only for pipeline updates

Pros:
  • Simpler than realtime subscriptions.
Cons:
  • Slower UI freshness or higher API load.
  • Inferior user feedback during ingestion lifecycle changes.

Consequences

Benefits:
  • Clear split between server state and UI-local state.
  • Efficient invalidation-based updates for pipeline events.
  • Familiar patterns for frontend developers.
Costs:
  • Multiple state technologies to maintain and document.
  • Realtime dependency requires client env and connection health handling.

Implementation notes

  • Query client initialization is in the providers layer.
  • Zustand stores handle UI-local state like voice sessions, sidebar, and graph visualization.
  • Realtime subscription and cache invalidation are implemented in a dedicated hook that maps pipeline events to React Query invalidations.
  • Supabase realtime client lifecycle is encapsulated in a dedicated module.