Skip to main content

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.

server/api/projects.get.ts
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

ModeCookie behavior
requiredExchange the request session; throw if no valid identity exists
optionalUse identity when valid, otherwise call anonymously
noneDo 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:

ts
const caller = serverConvex(event, {
  authToken: trustedToken,
})

Or exchange an explicit Better Auth cookie credential:

ts
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:

ts
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:

ts
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.