Build Complete REST API Feature with Nest JS (Using Prisma and Postgresql) from Scratch - Beginner-friendly - PART 2
Tags: nestjs, typescript, api, backend
Published: 2023-10-27 05:27:05

## Scope of discussion:
1. Understanding about `Guard` in Nest JS
2. Understanding about Nest JS decorator
3. How to handle private or public routes

---


In the first part, we've created some users endpoints:

`POST /users/register`,
`POST /users/login`,
`GET /users/me`,
`PATCH /users/:id`, and
`DELETE /users/:id`

but we haven't handled this endpoint `GET /users/me` yet.

Don't worry, we're going to fix it.

We'll only use `access_token` obtained from the login response to get the user's data. 

First, we must understand a `guard` in Nest JS. What does `guard` mean? According to the Nest JS documentation, `guard` is a class annotated with the `@Injectable()` decorator, which implements the `CanActivate` interface 
> [Read more about guard](https://docs.nestjs.com/guards).

And what is the purpose of `Guard`? Basically `Guard` is like a layer that is responsible for validating or updating any **incoming request** before being forwarded to their main process.

So for `me` endpoint, we're gonna validate the `access_token` and modify the information. Let's dig into this.

Let's create a new file called `auth.guard.ts` inside `src/common/guards/` folder:
```typescript
// src/common/guards/auth.guard.ts

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException();
    }
    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: process.env.JWT_SECRET,
      });
      // 💡 We're assigning the payload to the request object here
      // so that we can access it in our route handlers
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
```
In the code above, we created a class that implements `CanActivate` interface. Since we implement `CanActivate` interface, we need to define `canActivate` function that will be responsible for validating and updating the incoming request. There is also a function called `extractTokenFromHeader` for validating the `access_token` included through the header as a `Bearer token`.

Moving down, we'll see the `jwt` verification
```typescript
const payload = await this.jwtService.verifyAsync(token, {
  secret: process.env.JWT_SECRET,
});
```
To make this work, we need to add a new variable to our `.env` file `JWT_SECRET="super_secret_key"`
> We might need to update our `app.module.ts` as well by updating the `jwt` secret to use `.env` file `secret: 'super_secret_key',`

If all validation is passed, it will modify the incoming request information. In this case, **request** will receive `user` data from access_token `request['user'] = payload;`

After we created `auth.guard`. The next step is to register `auth.guard` to our root module (_a.k.a `app.module.ts`_). Open up `app.module.ts` and add the `auth.guard` to providers array:
```typescript
providers: [
  AppService,
  {
    provide: APP_GUARD,
    useClass: AuthGuard,
  },
],
```

Great! Now let's continue to `users.controller`. Open `src/modules/users/users.controller.ts` file and move to `me()` function:
```typescript
// This is before the changes
@Get('me')
me(): string {
  return 'Get my Profile!';
}
```

Let's modify that function by adding `@Request` decorator imported from `@nestjs/common`:
```typescript
@Get('me')
me(@Request() req) {
  return req.user;
}
```

Now we're ready to test `GET /users/me` endpoint:
![Users/me test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ckuzi4v3ozix531yv47t.png)

> You might not be able to access other endpoints (including login) since we registered `auth.guard` globally. We'll fix this in the section below.

Is that all about `GET /users/me` endpoint? Nope, we can still make an adjustment. If you take a look at the function, `req` doesn't have an explicit type `@Request() req`. Since we're using typescript to write our code, will be better to add explicit type.

Let's do this!
We need to create an interface:
`src/modules/users/interfaces/express-request-with-user.interface.ts`
```typescript
import { Request as ExpressRequest } from 'express';
import { UserPayload } from './users-login.interface';

export interface ExpressRequestWithUser extends ExpressRequest {
  user: UserPayload & { iat: number; exp: number };
}
```

Then we can use new interfaces to our `me` function
```typescript
@Get('me')
me(@Request() req: ExpressRequestWithUser): UserPayload {
  return req.user;
}
```

Finally, we're done with `GET /users/me` endpoint!

---

Let's move on to the last part of this section. We already created `auth.guard` and set it as a global `Guard`. But now we have a problem. If we access the login or register endpoint, we'll get `Unauthorized` response
```json
{
  "message": "Unauthorized",
  "statusCode": 401
}
```
As we will have some public endpoints (No need to pass `access_token`), this will be troublesome.

How do we fix that?

Now we must provide a mechanism for declaring routes as public. For this, we can create a custom decorator using the SetMetadata decorator factory function.

Create a new file `src/common/decorators/public.decorator.ts`:
```typescript
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
```

Update our `users.controller` by adding `@Public()` decorator in register and login endpoints:
```typescript
...

@Public() // <--- Set register as public route
@Post('register')
async registerUser(@Body() createUserDto: CreateUserDto): Promise<User> {
  // call users service method to register new user
  return this.usersService.registerUser(createUserDto);
}

@Public() // <--- Set login as public route
@Post('login')
loginUser(@Body() loginUserDto: LoginUserDto): Promise<LoginResponse> {
  // call users service method to login user
  return this.usersService.loginUser(loginUserDto);
}

...
```

Lastly, open again `auth.guard.ts` and add some modify:
```typescript
// src/common/guards/auth.guard.ts

export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,

    // 💡 See this line
    private reflector: Reflector,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 💡 See this line
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    // 💡 See this condition
    if (isPublic) {
      return true;
    }
    
    ...

  }
}
```

Now we're able to access login and register endpoints :)


The final step of this part.

After everything we've done here, let's finalize our `users` endpoints. So we want to prevent a user from updating or deleting another user's data, for example: in the `access_token`, it contains `user id` equal to 2. We don't want `user id` 2 able to change the name of `user id` 1.

To achieve that, let's create a new `Guard` called `is-mine.guard`
```typescript
// src/modules/users/users.controller.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class IsMineGuard implements CanActivate {
  constructor() {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();

    // 💡 We can access the user payload from the request object
    // because we assigned it in the AuthGuard
    return parseInt(request.params.id) === request.user.sub;
  }
}
```

We won't apply `is-mine.guard` globally like what we did in `auth.guard`, but we'll apply to routes manually.

Open up `users.controller` and add `@UseGuards(IsMineGuard)` decorator to our `updateUser` and`deleteUser`
```typescript
@Patch(':id')
  @UseGuards(IsMineGuard) // <--- 💡 Prevent user from updating other user's data
  async updateUser(
    @Param('id', ParseIntPipe) id: number,
    @Body() updateUserDto: UpdateUsertDto,
  ): Promise<User> {
    // call users service method to update user
    return this.usersService.updateUser(+id, updateUserDto);
  }

  @Delete(':id')
  @UseGuards(IsMineGuard) // <--- 💡 Prevent user from deleting other user's data
  async deleteUser(@Param('id', ParseIntPipe) id: number): Promise<string> {
    // call users service method to delete user
    return this.usersService.deleteUser(+id);
  }
```

![Forbidden resource](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u06xjbll5dww3n5jiz1k.png)

---

The full code of part 2 can be accessed here: https://github.com/alfism1/nestjs-api/tree/part-two

Moving on to part 3:
https://dev.to/alfism1/build-complete-rest-api-feature-with-nest-js-using-prisma-and-postgresql-from-scratch-beginner-friendly-part-3-3j34