serverConvex
Call Convex from a Nitro request with explicit authentication policy and request-scoped state.
serverConvex(event) creates one request-scoped caller with query, mutation, and action methods.
import { api } from '#convex/api'
import { serverConvex } from '#convex/server'
export default defineEventHandler(async (event) => {
return await serverConvex(event, { auth: 'required' }).query(api.projects.list)
})Authentication modes
| Mode | Cookie behavior |
|---|---|
required | Exchange the request session; throw if no valid identity exists |
optional | Use identity when valid, otherwise call anonymously |
none | Do not inspect or exchange auth cookies |
The server default is optional when no explicit principal is supplied.
Caller lifetime
One caller lazily owns:
- one token-resolution promise;
- one official
ConvexHttpClient; - one current request identity snapshot.
Do not store a caller in module scope or on a cross-request cache. Create it inside the handler.
Transport bounds
SSR queries and serverConvex use the same bounded official HTTP transport.
An incoming request abort cancels upstream response consumption. Query calls
have an 8-second deadline, mutations 15 seconds, and actions 60 seconds. These
fixed private limits are operation-aware and are not caller configuration.
Every query, mutation, and action response is capped at 1 MiB. A declared oversize response is rejected before its body is read; a streamed response is cancelled as soon as it crosses the same cap.
Explicit principal
An operator-controlled server workflow may provide an already obtained Convex JWT:
const caller = serverConvex(event, {
authToken: trustedToken,
})Or exchange an explicit Better Auth cookie credential:
const caller = serverConvex(event, {
credential: { type: 'cookie', value: cookieHeader },
})authToken and credential are mutually exclusive. Either one already means
required authentication, so auth must be omitted for an explicit principal.
The public type rejects these impossible combinations, and runtime validation
does the same for JavaScript and casts.
Never accept these values from an untrusted request body and forward them blindly. Raw Better Auth session tokens are not a public bearer-exchange credential. The package reserves bearer session handling for its private, marked Convex bridge.
Function arguments
The caller matches Convex's generated function-reference contract. Omit the
artificial {} for a function with no arguments:
const projects = await serverConvex(event).query(api.projects.list)Functions with declared arguments still require their exact generated object.
Errors
Calls reject with ConvexCallError for classifiable auth, transport, and structured Convex server failures. Invalid options throw ServerConvexValidationError before network access.
A rejected caller keeps its rejected token/client state. Create a new caller for an intentional retry.
Safe server diagnostics
The application already knows the operation, function reference, and request correlation context. Combine those static values with reviewed public error fields instead of logging the raw error cause:
import { ConvexCallError } from '@lupinum/better-convex-nuxt/errors'
import { getFunctionName } from 'convex/server'
const reference = api.projects.archive
try {
return await serverConvex(event, { auth: 'required' }).mutation(reference, { projectId })
} catch (error) {
if (error instanceof ConvexCallError) {
reportServerCallFailure({
correlationId: event.context.requestId, // application-owned
operation: 'mutation',
functionName: getFunctionName(reference),
kind: error.kind,
// Add `code` only after checking it against application-owned values.
})
}
throw error
}Do not include function arguments, results, cookies, headers, tokens, message,
data, or cause in generic telemetry. Function names expose application
topology, so keep them in server-only, access-controlled diagnostics.
The pinned Convex client uses indistinguishable plain Error values for
several client, protocol, and unstructured server failures. Those failures stay
unknown; do not infer a category from message text. Throw a structured
ConvexError with an application-owned, public-safe code for expected domain
or operator failures.
No client hydration
A serverConvex result returned from your API route is ordinary route data. It does not seed useConvexQuery or establish a subscription.
Use a composable directly in a page for normal SSR-to-live data. Use a server route when a real server boundary is required.