VelaVela
Techniques

Guards

Guards determine whether a request should be handled by the route handler.

Guards execute before the route handler and decide if the request should proceed. They implement the CanActivate interface and return a boolean indicating whether access is granted.

CanActivate Interface

import { Injectable, CanActivate, ExecutionContext } from '@velajs/vela';

@Injectable()
class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean | Promise<boolean> {
    const request = context.getRequest();
    const token = request.headers.get('authorization');
    return !!token;
  }
}

Execution Context

The ExecutionContext provides details about the current request pipeline:

MethodDescription
getClass()Returns the controller class
getHandler()Returns the route handler method
getContext<T>()Returns the platform-specific context
getRequest()Returns the incoming request object

Binding Guards

Apply guards at the method or controller level with @UseGuards():

import { Controller, Get, UseGuards } from '@velajs/vela';

@Controller('spirits')
@UseGuards(AuthGuard)
class SpiritsController {
  @Get()
  findAll() {
    return [];
  }

  @Get('admin')
  @UseGuards(RolesGuard)
  adminOnly() {
    return { admin: true };
  }
}

Global Guards

Register guards globally so they run on every route:

const app = await VelaFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());

Or use the APP_GUARD injection token to register them through the module system with full dependency injection:

import { APP_GUARD } from '@velajs/vela';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
class AppModule {}

Resolution Order

Guards execute in order: global → controller → handler. If any guard returns false, the framework throws a ForbiddenException and the request is rejected immediately.

Role-Based Authorization

A common pattern is restricting routes based on user roles. Use SetMetadata to attach role requirements and Reflector to read them in a guard:

import { SetMetadata } from '@velajs/vela';

const Roles = (...roles: string[]) => SetMetadata('roles', roles);
import {
  Injectable, CanActivate, ExecutionContext,
  Reflector, ForbiddenException,
} from '@velajs/vela';

@Injectable()
class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext) {
    const roles = this.reflector.get<string[]>('roles', context);
    if (!roles) return true;

    const request = context.getRequest();
    const userRole = request.headers.get('x-role');
    if (!roles.includes(userRole)) {
      throw new ForbiddenException('Insufficient role');
    }
    return true;
  }
}

Apply the roles decorator to routes:

@Controller('spirits')
@UseGuards(RolesGuard)
class SpiritsController {
  @Get()
  findAll() {
    return [];
  }

  @Post()
  @Roles('admin')
  create(@Body() body: { name: string }) {
    return body;
  }
}
Edit on GitHub

Last updated on

On this page