Skip to content

Context

Every procedure handler receives a ctx object with the current request, database client, authenticated user, and any state your plugins add. Each request gets its own isolated scope, extensible through TypeScript declaration merging.

Every handler receives:

.query(async ({ input, ctx }) => {
ctx.request // Fastify request
ctx.reply // Fastify reply
ctx.db // Prisma client (with ORM plugin)
ctx.user // Authenticated user (with auth plugin)
})

Use TypeScript declaration merging:

src/types.ts
declare module '@veloxts/core' {
interface BaseContext {
tenant: Tenant;
logger: Logger;
}
}

Then populate in a plugin or middleware:

app.addHook('onRequest', async (request, reply) => {
request.context = {
...request.context,
tenant: await getTenant(request),
logger: createLogger(request),
};
});

Guards receive the full context:

const isTenantAdmin = defineGuard({
name: 'isTenantAdmin',
check: (ctx) => ctx.user?.role === 'admin' && ctx.tenant?.id === ctx.user?.tenantId,
message: 'Must be tenant admin',
});