Skip to main content

Authenticated server route

Call Convex from Nitro with the incoming user's identity and a safe HTTP contract.

Result

A same-origin API route requires the Better Auth session, calls a protected Convex query, and maps failures without exposing credentials.

server/api/account/export.get.ts
import { api } from '#convex/api'
import { serverConvex, toConvexH3Error } from '#convex/server'

export default defineEventHandler(async (event) => {
  setHeader(event, 'cache-control', 'private, no-store')
  try {
    return await serverConvex(event, { auth: 'required' }).query(api.accounts.exportData)
  } catch (error) {
    throw toConvexH3Error(error)
  }
})

toConvexH3Error answers 401 for a missing or invalid session, 502 for a transport failure, and the application's 4xx status for a ConvexError. The response data is the serialized ConvexCallError without functionName, so the browser never learns which Convex function the route called. In the browser, normalizeConvexError() turns the $fetch error back into a ConvexCallError:

ts
import { isConvexCallError, normalizeConvexError } from '@lupinum/better-convex-nuxt/errors'

try {
  exportFile.value = await $fetch('/api/account/export')
} catch (raw) {
  const error = normalizeConvexError(raw)
  if (isConvexCallError(error, 'UNAUTHENTICATED')) await navigateTo('/auth/signin')
  else exportError.value = 'Export unavailable'
}

Security boundary

  • serverConvex exchanges only the request's relevant Better Auth cookies.
  • auth: 'required' refuses anonymous fallback.
  • accounts.exportData rechecks identity and product access.
  • The response is private and not cached.
  • Public errors do not contain cookies, tokens, or upstream bodies.
  • Use requireConvexUser(event) instead when the route needs only the signed-in user, not Convex data.

Verify

  • No-cookie request returns 401 with code UNAUTHENTICATED.
  • Valid session returns only that user's export.
  • Revoked/invalid session does not fall back to anonymous.
  • A transport failure returns the safe 502 contract.