ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Nest.js] Life Cycle - advanced approach
    Nest.js 2021. 9. 29. 15:12
    Do we know why they are seperated into several components?
    Is it possible to implement within any component by ignoring their role?

     

    LET'S SEE MORE DETAILS OF Nest.js Life Cycle
    RIGHT NOWWWWWWW !

     

    - Nest.js Life Cycle from request to response


    - Detail

     

    1.Middleware

    Middleware is called only before the route handler is called.
    You have access to the response object, but you don't have the result of the route handler.
    They are basically express middleware functions.

    Registration

    • Module-scoped : 
      export class AppModule implements NestModule {
        configure(consumer: MiddlewareConsumer) {
          consumer
            .apply(LoggerMiddleware)
            .forRoutes('cats');
        }
      }​
    • Global-scoped : app.use(XXX) in main.ts

    Examples

    Conclusion

    The registration of middleware is very flexible, for example: apply to all routes but one etc.
    But since they are registered in the module,
    we might not realize it applies to your controller when we're looking at its methods.
    It's also great that you can make use of all the express middleware libraries that are out there.

    More details

    Look at my Middleware content to go much deeper ! - https://jinseo-copy-and-paste.tistory.com/25

     

    2. Guards

    Guards is literally guarding a request whether user is validated or user has a permission.
    We can make a barrier for a secure purpose via Guards.

    Registration

    • Controller-scoped : @UseGuards(XXX)
    • Method-scoped : @UseGuards(XXX)
    • Global-scoped : app.useGlobalGuards(XXX) in main.ts

    Examples

    • authentification/authorization of a request
    • send back a 403 if the component returns false
    • JWT, Passport, JwtStrategy, AuthGuard()

    Conclusion

    Guards component specific for authorization and authentification of a request.
    send back a 403 if the component returns false

    More details

    look at my previous contents from 9 ~ 12 for more details

    Authentification - https://docs.nestjs.com/security/authentication
    Authorization - https://docs.nestjs.com/security/authorization

     

    3. Interceptors

    Pre
    Post

    This component has only Pre and Post Segment !! 
    Interceptors have access to response/request before and after the route handler is called.
    Interceptors are components that are super flexible having pre and post segments.

    Registration

    • Controller-scoped : @UseInterceptors(XXX)
    • Method-scoped : @UseInterceptors(XXX)
    • Global-scoped : app.useGlobalInterceptors(XXX) in main.ts

    Examples

      Pre-Interceptor

    • Caching
    • Logging : Starting/Setting up log timing

      Post-Interceptor

    • Caching : setting up cache values
    • Logging : finishing time it takes
    • ResultMapping : Transform null to [] or wrap result in a response object: users -> {users: users}
    • Cookie : set cookies
    • Dynamic rendering and sorting of other things

    Conclusion

    I like that the registration is closer to the route handlers compared to middleware.
    We can enjoy with Interceptors to measure, calculate, or compare after its route handler.

    But there are some limitations, for example, you cannot set the response code or alter the response with Interceptors when you send the response with the library-specific @Res() object in your route handler, see docs.

     

    4. Pipes

    Really useful for checking the shape of the requests.
    Pipes is for transformation and validation of request components.

    Registration

    • Directly in the controller class with  @UseFilters() controller- or method-scoped
    • Globally app.useGlobalFilters() in your main.ts
    • Controller-scoped : @UsePipes(XXX)
    • Method-scoped : @UsePipes(XXX)
    • parameter : 
      @Get(':id')
      async findOne(@Param('id', ParseIntPipe) id: number) {
        return this.catsService.findOne(id);
      }​
    • Global-scoped : app.useGlobalFilters(XXX) in main.ts

    Examples

      built-in pipes

    • ValidationPipe
    • ParseIntPipe
    • ParseFloatPipe
    • ParseBoolPipe
    • ParseArrayPipe
    • ParseUUIDPipe
    • ParseEnumPipe
    • DefaultValuePipe

    Conclusion

    Pipes operate on the argument being processed by a controller route handler.
    Pipe is interposed just before a method is invoked,
    and pipes receives the arguments destined for the method and operates on them.

     

    5. Exception Filters

    Exception Filters are called after the route handler and after the interceptors.
    They are the last place to make changes before a response goes out.

    Registration

    • Controller-scoped : @UseFilters(XXX)
    • Method-scoped : @UseFilters(XXX)
    • Global-scoped : app.useGlobalFilters(XXX) in main.ts

    Examples

    • UnauthorizedFilter: Map to an easy to understand message for the user
    • NotFoundFilter: Map all routes that are not found (not part of your api) to your index.html.

    Conclusion

    The basic use case for exception filters are giving understandable error messages (hiding technical details). But there are also other creative ways of usage:
    When you serve a single page application,
    then typically all routes should redirect to index.html except the routes of your API.
    Here, you can redirect on a NotFoundException. Some might find this clever others hacky. Your choice. ;-)


    QUESTIONS ?!

     

    1. I feel like Middleware and interceptor are stilllllll the same !!!

    Why? Both of them have access to Request and Response !!

    Let's make it sure that we know their differencies and use them in proper way.

     

    1.1 code comparison

    - middleware

    import { Injectable, NestMiddleware } from '@nestjs/common';
    import { Request, Response, NextFunction } from 'express';
    
    @Injectable()
    export class LoggerMiddleware implements NestMiddleware {
      use(req: Request, res: Response, next: NextFunction) {
        console.log('Request...');
        next();
      }
    }

     

    - interceptor

    import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
    import { Observable } from 'rxjs';
    import { tap } from 'rxjs/operators';
    
    @Injectable()
    export class LoggingInterceptor implements NestInterceptor {
      intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
        console.log('Before...');
    
        const now = Date.now();
        return next
          .handle()
          .pipe(
            tap(() => console.log(`After... ${Date.now() - now}ms`)),
          );
      }
    }

     

    1.2 once VS twice

    - As you saw the above Life cycle, Middleware is executed only before route handler(Controller).
      On the other hand, Interceptors do more some tasks right before the handler and right after the handler.
      So, it is working for comparing its before and after differency, such as meassure time it takes.   

    1.3 request and response

    - Even though middleware has access to response object, it could not have the result after the handler. 
      On the other hand, interceptor has the result after the handler,
      so it could mutate or wrap more user-friendly/developer-friendly result.

     

    2. Authentification/Authorization in Middleware or Guards?!

    - Authorization/Authentication has typically been handled by middleware in traditional Express applications.

    - Middleware is a fine choice for authentication,
      since things like token validation and attaching properties to the request object are not strongly connected
      with a particular route context (and its metadata).

    - But middleware, by its nature, is dumb.
      It doesn't know which handler will be executed after calling the next() function.

    - On the other hand, Guards have access to the ExecutionContext instance,
      and thus know exactly what's going to be executed next.
      They're designed, much like exception filters, pipes, and interceptors, to let you interpose processing logic
      at exactly the right point in the request/response cycle, and to do so declaratively.
      This helps keep your code *DRY and declarative

     

    *DRY : Don’t Repeat Yourself (DRY) is a software development principle, the main aim of which is to reduce repetition of code.

    * WET : Write Everything Twice (WET) is a cheeky abbreviation to mean the opposite i.e. code that doesn’t adhere to DRY principle.


    Let's begin with the questions that I have got in the top.

     

    "Do we know why they are seperated into several components?" , 
    "Is it possible to implement within any component by ignoring their role?"

    - Now we know each component has its own characteristics.

    - Let's see in real,
      Imagine, we implement all in one.
      you have to explain that to each person who joins your project.
      When I see a guard, I know it’s authentication/authorization.
      When I see a pipe, I know there’s validation and transformation.
      This helps onboarding of new members.

      Also, It is NOT based on a Single Responsibility Principle(in SOLID) of Object-Oriented Programming !!!

    - That is why we implement features separated into those components !!!!!!!

     

     

     

     

     

     

    'Nest.js' 카테고리의 다른 글

    [Nest.js] Life Cycle - 2 Guards  (0) 2021.10.07
    [Nest.js] Life Cycle - 1 Middleware  (0) 2021.10.01
    [Nest.js] simple project  (0) 2021.09.27
    [Nest.js] 16 - Log with Winston and WebHook  (0) 2021.09.25
    [Nest.js] 15 - Configuration  (0) 2021.09.22
Designed by Tistory.