Skip to content

Guards

Guards decide whether a request is allowed to proceed. They run before pipes and before the handler. A guard returns True to allow the request or False to block it.

Basic Guard

Python
from typing import Awaitable, Union

from nestipy.common import CanActivate, Injectable
from nestipy.core import ExecutionContext


@Injectable()
class AuthGuard(CanActivate):
    def can_activate(self, context: ExecutionContext) -> Union[Awaitable[bool], bool]:
        req = context.switch_to_http().get_request()
        return req.headers.get("authorization") is not None

If a guard returns False, Nestipy raises an HTTP 401 error by default.

Binding Guards

Guards can be applied at four levels:

  • Controller level with @UseGuards
  • Method level with @UseGuards
  • Module level with AppKey.APP_GUARD
  • Global level with app.use_global_guards
Python
from nestipy.common import Controller, Post, UseGuards


@UseGuards(AuthGuard)
@Controller("cats")
class CatsController:
    @UseGuards(AuthGuard)
    @Post()
    async def create(self):
        pass

Module-level guard:

Python
from nestipy.common import Module, ModuleProviderDict
from nestipy.core import AppKey


@Module(
    providers=[
        ModuleProviderDict(
            AppKey.APP_GUARD,
            use_class=AuthGuard,
        )
    ]
)
class AppModule:
    pass

Global guard:

Python
from nestipy.core import NestipyFactory

app = NestipyFactory.create(AppModule)
app.use_global_guards(AuthGuard)

Role-based Guard Example

Python
import typing
from typing import Union, Awaitable

from nestipy.metadata import SetMetadata, Reflect
from nestipy.common import CanActivate, UseGuards, Controller, Post, Injectable
from nestipy.core import ExecutionContext

ROLES = "ROLES"


def Roles(roles: list[str]):
    return SetMetadata(ROLES, roles, as_list=True)


@Injectable()
class RolesGuard(CanActivate):
    async def can_activate(self, context: ExecutionContext) -> Union[Awaitable[bool], bool]:
        handler = context.get_handler()
        controller = context.get_class()
        req = context.switch_to_http().get_request()
        roles = list(
            set(
                Reflect.get_metadata(controller, ROLES, [])
                + Reflect.get_metadata(handler, ROLES, [])
            )
        )
        user_roles = req.user.roles if req.user is not None else []
        return len(set(typing.cast(list[str], user_roles)) & set(roles)) > 0


@UseGuards(RolesGuard)
@Controller("cats")
class CatsController:
    @Post()
    @Roles(["admin"])
    async def create(self):
        pass

Tips

  • Guards should be fast and side-effect free.
  • Use metadata decorators to keep guard logic generic.
  • Combine global guards with method-level overrides for precise control.

Support us

Nestipy is a project released under the MIT license, meaning it's open source and freely available for use and modification. Its development thrives with the generous contributions of these fantastic individuals.