Guards
Guards run before your procedure handler and reject the request if the caller doesn’t meet the required conditions — whether that’s being logged in, having a specific role, or satisfying a custom check. They compose with allOf, anyOf, and not combinators for complex authorization logic.
Built-in Guards
Section titled “Built-in Guards”authenticated
Section titled “authenticated”Requires a logged-in user:
import { authenticated } from '@veloxts/auth';
getProfile: procedure() .guard(authenticated) .query(({ ctx }) => ctx.user),hasRole(role)
Section titled “hasRole(role)”Requires a specific role:
import { hasRole } from '@veloxts/auth';
deleteUser: procedure() .guard(authenticated) .guard(hasRole('admin')) .mutation(handler),hasPermission(permission)
Section titled “hasPermission(permission)”Requires a specific permission:
import { hasPermission } from '@veloxts/auth';
updateSettings: procedure() .guard(authenticated) .guard(hasPermission('settings:write')) .mutation(handler),Custom Guards
Section titled “Custom Guards”Simple Form with guard()
Section titled “Simple Form with guard()”The quickest way to create a custom guard:
import { guard } from '@veloxts/auth';
// Check function + message (most common)const isVerified = guard( (ctx) => ctx.user?.emailVerified === true, 'Email verification required');
// UsageupdateEmail: procedure() .guard(authenticated) .guard(isVerified) .mutation(handler),Fluent Builder
Section titled “Fluent Builder”For more control, use the fluent builder:
import { guard } from '@veloxts/auth';
const isPremium = guard((ctx) => ctx.user?.subscription === 'premium') .named('isPremium') .msg('Premium subscription required') .status(402);Full Form with defineGuard
Section titled “Full Form with defineGuard”For maximum explicitness:
import { defineGuard } from '@veloxts/auth';
const isVerified = defineGuard({ name: 'isVerified', check: (ctx) => ctx.user?.emailVerified === true, message: 'Email verification required', statusCode: 403,});Guard Composition
Section titled “Guard Composition”allOf - All guards must pass
Section titled “allOf - All guards must pass”import { allOf } from '@veloxts/auth';
const adminAndVerified = allOf(hasRole('admin'), isVerified);
sensitiveOperation: procedure() .guard(authenticated) .guard(adminAndVerified) .mutation(handler),anyOf - At least one guard must pass
Section titled “anyOf - At least one guard must pass”import { anyOf } from '@veloxts/auth';
const adminOrModerator = anyOf(hasRole('admin'), hasRole('moderator'));
moderateContent: procedure() .guard(authenticated) .guard(adminOrModerator) .mutation(handler),not - Invert a guard
Section titled “not - Invert a guard”import { not } from '@veloxts/auth';
const notBanned = not(defineGuard({ name: 'isBanned', check: (ctx) => ctx.user?.banned === true, message: 'Account is banned',}));Understanding ctx.user
Section titled “Understanding ctx.user”Chaining Guards
Section titled “Chaining Guards”Guards run in order. If any fails, subsequent guards don’t run:
getSecretData: procedure() .guard(authenticated) // 1. Must be logged in .guard(isVerified) // 2. Must have verified email .guard(hasRole('admin')) // 3. Must be admin .guard(notBanned) // 4. Must not be banned .query(handler),Combined .guards() Method
Section titled “Combined .guards() Method”Add multiple guards at once for cleaner code:
import { authenticated, hasRole, emailVerified } from '@veloxts/auth';
// Multiple guards in one callgetSecretData: procedure() .guards(authenticated, emailVerified, hasRole('admin')) .query(handler),
// Equivalent to chaining .guard() callsgetSecretData: procedure() .guard(authenticated) .guard(emailVerified) .guard(hasRole('admin')) .query(handler),Error Responses
Section titled “Error Responses”When a guard fails:
{ "error": { "message": "Email verification required", "code": "FORBIDDEN" }}HTTP status code is set based on guard configuration (default: 403).
Resource-Based Guards
Section titled “Resource-Based Guards”For per-resource authorization, use policies instead:
// Guard: "Is user an admin?" (role-based).guard(hasRole('admin'))
// Policy: "Can user edit THIS post?" (resource-based).guard(authorize('posts', 'edit'))See Policies for resource-based authorization.