Skip to main content

Design Decisions

Understand the deliberate constraints behind the public runtime.

These decisions describe current public behavior. They are not a roadmap.

SSR first, live after hydration

Decision: Queries enable SSR and subscriptions by default.

Problem: A browser-only subscription produces no server data. A server-only fetch becomes stale after hydration.

Benefit: One composable provides initial HTML and later live updates.

Cost: The lifecycle performs an SSR HTTP query and a browser subscription.

Alternative: Set server: false or subscribe: false per query or through module defaults.

Convex owns subscription deduplication

Decision: The module does not maintain a parallel subscription registry.

Problem: A second registry duplicates client state, reference counting, and cleanup behavior already owned by Convex.

Benefit: Fewer state owners and less private coupling.

Cost: Composable instances still own separate Vue-visible state even when Convex deduplicates wire work.

Alternative: Use defineSharedConvexQuery when consumers must share the same Vue state.

Identity changes replace the primary client

Decision: A stable identity-key change retires the old primary client and clears identity-owned query state.

Problem: Convex client state can contain cached query results and pending optimistic updates created under the previous user.

Benefit: User A's client state cannot become user B's local starting state.

Cost: In-flight imperative calls may reject with IDENTITY_CHANGED during the transition.

Alternative: Same-user token refresh keeps the client because the stable identity did not change.

useConvex() exposes a stable narrow handle

Decision: The handle contains query, mutation, action, and onUpdate, not the raw client.

Problem: A captured raw client becomes closed after identity replacement and exposes lifecycle methods owned by the runtime.

Benefit: Calls always target the current client; subscriptions can rebind.

Cost: Application code cannot call setAuth, clearAuth, or close.

Alternative: Use the high-level composables and useConvexConnectionState() for supported needs.

Authorization stays in the application

Decision: The library exposes identity and route UX primitives but no product permission framework.

Problem: Only the product knows its resources, roles, membership rules, and domain invariants.

Benefit: Backend policy stays near Convex data and remains testable without frontend orchestration.

Cost: Applications implement reusable authorization helpers themselves.

Alternative: Use verified recipes as patterns, not as a second policy engine.

Server calls use request-scoped official primitives

Decision: serverConvex creates one request-scoped caller using the official HTTP client.

Problem: Reusing a caller can mix credentials across requests. Manual protocol code adds an unnecessary transport owner.

Benefit: Credential resolution and call lifetime match the Nuxt request.

Cost: Server calls do not share browser subscription state or hydrate client composables automatically.

Alternative: Call Convex directly from the browser for normal interactive data.

No second entity store

Decision: The module relies on Convex query state and Nuxt payload reuse instead of adding a general entity cache.

Problem: Another cache needs invalidation, identity partitioning, optimistic reconciliation, and a rebuild story.

Benefit: Server-backed data has one canonical owner.

Cost: UI state and server data use different tools.

Alternative: Keep UI-only state in local refs or an application store.