Skip to content
Documentation GitHub
Architecture

Frontend as Dumb Pipe: No Business Logic in React Layer

Frontend as Dumb Pipe: No Business Logic in React Layer

Principle

The React frontend contains no business logic — only UI/UX behavior. All meaningful data handling is delegated to the Tauri backend via invoke().

Application to Third-Party Clients

Third-party JS clients (e.g., Supabase JS SDK) are acceptable infrastructure for what they do well — WebSocket lifecycle management, reconnection with backoff, channel subscriptions — but they must act as a dumb pipe.

Correct pattern (Realtime sync):

// realtime-sync.ts — receives event, immediately delegates to backend
.on("postgres_changes", { event: "INSERT", table: "block_updates", ... },
() => {
// NO payload processing. NO business logic.
// Just tell the backend "something changed."
invoke("trigger_pull_sync");
}
)

The frontend doesn’t inspect the event payload, doesn’t decide what to do with it, doesn’t transform data. It notifies the backend, and the backend (Rust) handles everything.

Incorrect pattern (anti-pattern):

// DON'T DO THIS — business logic in frontend
.on("postgres_changes", { event: "INSERT", table: "block_updates", ... },
(payload) => {
const update = parseBlockUpdate(payload.new); // Processing in frontend
if (update.deviceId !== currentDeviceId) { // Decision in frontend
mergeIntoLocalStore(update); // State mutation in frontend
}
}
)

Why This Matters

  1. Single source of truth: All sync logic lives in Rust — no divergent implementations
  2. Testability: Rust logic is unit-testable without browser environment
  3. Security: Sensitive operations stay in the backend where they can be audited
  4. Consistency: The Clean Architecture layers (Domain → Application → Infrastructure) enforce this in Rust; the frontend is just another adapter

Auth Token Bridge

The Supabase JS client needs a valid JWT for RLS-filtered Realtime events. Auth sessions are owned by the Rust backend (keychain). The bridge:

  1. Tauri command exposes the access token to the frontend
  2. Frontend calls supabase.auth.setSession() with the token
  3. On token refresh in Rust, emit a Tauri event so frontend can update
  4. Frontend never manages tokens directly — it receives them from the backend

References

  • Linear: INK-149 (Realtime hookup), INK-150 (auth token bridge)
  • Existing: apps/desktop/src-react/lib/realtime-sync.ts — correct pattern
  • Existing: apps/desktop/src-react/hooks/useRealtimeSync.ts — ready but unmounted

Was this page helpful?