Skip to content

Policies

Guards check who the user is; policies check what they can do with a specific resource. A policy defines rules like “users can edit their own posts” or “only admins can delete comments,” keeping authorization logic centralized rather than scattered across handlers.

import { definePolicy } from '@veloxts/auth';
const PostPolicy = definePolicy('Post', {
create: ({ user }) => user.role === 'editor' || user.role === 'admin',
update: ({ user, resource }) => resource.authorId === user.id,
delete: ({ user, resource }) => resource.authorId === user.id || user.role === 'admin',
});

Each action is a PolicyActionRef with .actionName, .resourceName, and .check():

PostPolicy.update.actionName // 'update'
PostPolicy.update.resourceName // 'Post'
PostPolicy.update.check(user, post) // boolean | Promise<boolean>

The recommended way to enforce policies — checked automatically before the handler runs:

import { procedure } from '@veloxts/velox';
import { authenticated } from '@veloxts/auth';
updatePost: procedure()
.input(z.object({ id: z.string(), data: UpdatePostSchema }))
.guard(authenticated)
.use(loadPost) // adds { post: Post } to context
.policy(PostPolicy.update) // checked before handler — throws ForbiddenError on failure
.mutation(async ({ input, ctx }) => {
return ctx.db.post.update({
where: { id: input.id },
data: input.data,
});
}),

The policy looks up the resource from context by name — PostPolicy looks for ctx.post. Forgetting .use(loadPost) before .policy() produces a TypeScript error when the resource name is a string literal.

For complex conditional logic inside handlers, use can() and cannot():

import { can, cannot } from '@veloxts/auth';
if (await can(ctx.user, 'update', 'Post', post)) {
// User can update
}
if (await cannot(ctx.user, 'delete', 'Post', post)) {
// User cannot delete
}
FeatureGuardsPolicies (.policy())Policies (can()/cannot())
ScopeRequest-levelResource-levelResource-level
Example”Is user admin?""Can user edit THIS post?""Can user edit THIS post?”
RunsBefore handlerBefore handlerInside handler
StyleDeclarativeDeclarativeImperative