-
Notifications
You must be signed in to change notification settings - Fork 0
guards
With this chapter, if you want to follow along, check out the guards-start
branch and continue to the following along section.
If you just want a final version of the project as of the end of the chapter, check out the guards-end
branch, and proceed to the upon completion section.
Refer to the comments below, corresponding to chapter sections, to guide you through the code changes for nest cats as you proceed through the docs chapter.
Read through the Pipes chapter until you get to the Role-based authentication section, then return here.
When you get to the Role-based authentication portion of the docs, go ahead and create the role.guard.ts
file as described in this section. As before, we'll create a new folder in src/common
for guards. After creating that folder, our project structure should look like:
nest-cats
└───src
└───cats
│ └───dto
│ └───interfaces
└───common
└───filters
└───guards
└───middleware
└───pipes
Once you've created the role.guard.ts
file, go ahead and bind the guard almost as described in the Binding guards section of the docs. To make things a little smoother down the road, let's bind the guard to a single endpoint - POST /cats
. Our POST /cats
endpoint handler in src/cats/cats.controller.ts
should now look like this:
@Post()
@UseGuards(RolesGuard)
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
As usual, you'll also need to import UseGuards
and RolesGuard
.
At this point, the guard we created will fire, but it always returns true
from the canActivate()
function, so we won't see any change in behavior. Try creating and querying cats to see this for yourself. Don't worry, we'll eventually tie these pieces together so you can see the guard in action.
When you get to the Reflection section of the docs, read through until you understand the concept of creating a custom roles decorator.
Then, create roles.decorator.ts
in src/common/decorators
(by now, you know that you'll first create a decorators
folder in src/common
), and add the code as shown in the docs.
Follow the instructions to update RolesGuard
in src/common/guards/role.guard.ts
in the Putting it all together section of the docs.
Finally, import the Roles
custom decorator and apply the admin
role to the POST /cats
endpoint as shown at the end of the Reflection section. Your POST /cats
route handler should now look like this:
@Post()
@Roles('admin')
@UseGuards(RolesGuard)
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
We've now got a functioning guard on our POST /cats
route. Test it as discussed below.
Test our POST /cats
route with a valid request:
http POST :3000/cats name=Fred age:=3 breed='Alley Cat'
You should receive an error like:
{
"error": "Forbidden",
"message": "Forbidden resource",
"statusCode": 403
}
This shows us that our guard is functioning; we do not have the role admin
, as required by the route.
At this point, we should pause to ask "So... just exactly how would we get the role admin
on the route?". We've sort of approached this problem in reverse, so let's take a short step back. As mentioned in the docs, it's common practice in Express apps to attach a user object, including properties like role
, to the request
object. This opens up the topic of authentication -- in itself, a rather large topic -- which we cover in detail in a few places:
- In the Authentication chapter, we show how to do this with JWTs
- In the Sessions article blog post, we show how to do this with sessions and cookies
Neither of those tie in the roles we built here, so we'll add some commentary and code that ties these two pieces together when we get to the authentication chapter.
For now, you may want to comment out the @UseGuards(RolesGuard)
decorator so it doesn't interfere with remaining chapters.
Next up is the Interceptors chapter.