Guards
Guards protect procedures by checking authorization before the handler runs. If a guard fails, the request is rejected.
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”Create guards with defineGuard:
import { defineGuard } from '@veloxts/auth';
const isVerified = defineGuard({ name: 'isVerified', check: (ctx) => ctx.user?.emailVerified === true, message: 'Email verification required', statusCode: 403,});
// UsageupdateEmail: procedure() .guard(authenticated) .guard(isVerified) .mutation(handler),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),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.