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.