Skip to main content

Request Lifecycle

Follow one query from the incoming Nuxt request through live updates and cleanup.

An awaited query can begin on the Nuxt server and continue in the browser. The Vue component observes one state object while the transport changes underneath it.

ts
const result = await useConvexQuery(api.projects.list, {})

1. Nuxt creates the request context

During SSR, Nuxt creates a request-scoped application instance. Module runtime configuration and, when enabled, the Better Auth session belong to this request.

No browser ConvexClient is reused on the server.

2. Authentication settles

The default query auth mode is optional. It waits for initial auth settlement, then runs with the signed-in identity or anonymously.

required waits and remains idle if the request is anonymous. none does not inspect auth and always runs anonymously.

3. The server executes the query

With server: true, the module executes the Convex query over HTTP. The returned value or normalized error becomes Nuxt async data.

At this point:

FieldTypical value
pendingfalse after the awaited call resolves
statussuccess or error
dataQuery result or null
isStalefalse

4. Nuxt renders and serializes

Vue renders with the query result. Nuxt serializes the async-data result into the payload sent with the page.

ConvexCallError has a public serialized shape. Its runtime-only cause is not placed in the payload.

5. Vue hydrates the existing result

The browser reads the Nuxt payload before mounting the component. The initial result is reused instead of replaced by a fresh client loading state.

Hydration requires the server and client to render compatible markup. Branch on explicit status and provide stable fallback structures when needed.

6. The subscription starts

With subscribe: true, the browser subscribes through the current Convex client. The initial live result may equal the SSR result. Later Convex updates replace data and re-run any configured transform.

Convex owns wire-level subscription deduplication. Each composable owns its Vue-visible state and listener lifecycle.

7. Arguments or identity may change

Reactive arguments start the next execution. With keepPreviousData: true, the last successful value can remain visible and isStale becomes true while the new result is pending.

An identity change is stricter. Identity-owned data is cleared synchronously and cannot be kept as stale data for the next user.

8. The scope ends

When the component scope ends, its subscription listener stops. A shared query definition instead lives for the Nuxt app lifetime and is disposed with that app.

The per-app runtime closes owned clients during app teardown.

Browser-only variation

With server: false, the server does not fetch data. The browser performs the first execution. Plan a loading state that renders correctly on both sides.

See SSR, hydration, and real-time for the execution-mode decision.