Template - API
WelcomePrerequisitesNodeJS
  • Welcome!
    • About me
  • Prerequisites
    • Installation and Database Setup
    • Installing Node.js on the Server
  • NodeJS
    • 🆓Getting Started with the Free Template
    • 🎗️Premium Template - REST API
      • Structure
      • Middlewares and Guards
      • Core Modules
        • Prisma with NestJS
        • Swagger with NestJS
      • Dynamic Module
        • Create or Import Module
  • Modules
    • 🎗️Websockets
    • 🎗️Mailer
    • 🎗️Upload
Powered by GitBook
On this page
  • Introduction to Middlewares and Guards
  • Setting Up Middlewares in NestJS
  • Creating Guards in NestJS
  • Using Middlewares and Guards Together
  1. NodeJS
  2. Premium Template - REST API

Middlewares and Guards

We'll cover setting up Middlewares and Guards, applying them to routes, and ensuring your code adheres to SOLID principles for maintainability and scalability.


This is a tutorial that provides an overview of using the tool. There are many ways to code the logic and organize your code. It's up to you to design your API according to your specific needs. My role is to guide you and offer a solid foundation.

Introduction to Middlewares and Guards

Middlewares and Guards are fundamental components in NestJS that help manage request processing and authorization.

  • Middlewares: Functions that execute before the route handler, allowing you to manipulate the request and response objects. Common use cases include logging, authentication, and request validation.

  • Guards: Provide a way to control access to route handlers based on specific conditions. They are typically used for authorization, ensuring that only authenticated or authorized users can access certain endpoints.

Integrating Middlewares and Guards effectively enhances the security, maintainability, and scalability of your NestJS applications.


Setting Up Middlewares in NestJS

Step 1: Create a Middleware

To create a Middleware in NestJS, you can use the Nest CLI or manually create the necessary files.

Using Nest CLI:

nest generate middleware logger

This command generates a logger.middleware.ts file in the src directory.

Manual Creation:

Create a new directory for middlewares if it doesn't exist:

mkdir src/middlewares
touch src/middlewares/logger.middleware.ts

Example: Logger Middleware

// src/middlewares/logger.middleware.ts

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) {
    const { method, originalUrl } = req;
    const userAgent = req.get('user-agent') || '';
    const ip = req.ip;

    console.log(`[${new Date().toISOString()}] ${method} ${originalUrl} - ${userAgent} - ${ip}`);

    next();
  }
}

Explanation:

  • @Injectable(): Marks the class as a provider that can be injected into other classes.

  • NestMiddleware: Interface that requires the use method.

  • use Method: Receives Request, Response, and NextFunction objects. It logs request details and calls next() to pass control to the next middleware or route handler.

Step 2: Apply Middleware to Routes

Middlewares can be applied globally or to specific routes/modules.

Global Middleware:

To apply a middleware globally, modify the main.ts file.

// src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggerMiddleware } from './middlewares/logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.use(new LoggerMiddleware().use);

  await app.listen(3000);
}
bootstrap();

Specific Middleware:

To apply middleware to specific routes, configure it within a module.

Example: Applying Middleware in AppModule

// src/app.module.ts

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { LoggerMiddleware } from './middlewares/logger.middleware';

@Module({
  imports: [UsersModule],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'users', method: RequestMethod.ALL });
  }
}

Explanation:

  • MiddlewareConsumer: Provides methods to apply middlewares to routes.

  • apply Method: Specifies which middleware to apply.

  • forRoutes Method: Defines the routes where the middleware should be applied. In this example, the LoggerMiddleware is applied to all methods (RequestMethod.ALL) on the users route.

Step 3: Configuring Middleware for Specific Routes

You can apply middlewares to multiple routes or controllers as needed.

Example: Applying Middleware to Multiple Routes

// src/app.module.ts

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { ProductsModule } from './products/products.module';
import { LoggerMiddleware } from './middlewares/logger.middleware';

@Module({
  imports: [UsersModule, ProductsModule],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(
        { path: 'users', method: RequestMethod.ALL },
        { path: 'products', method: RequestMethod.ALL },
      );
  }
}

Explanation:

  • The LoggerMiddleware is now applied to both users and products routes, logging requests for both.


Creating Guards in NestJS

Guards are used to determine whether a request should be handled by the route handler or not based on specific conditions, such as user authentication and authorization.

Step 1: Create a Guard

You can create a Guard using the Nest CLI or manually.

Using Nest CLI:

nest generate guard auth

This command generates an auth.guard.ts file in the src directory.

Manual Creation:

Create a new directory for guards if it doesn't exist:

mkdir src/guards
touch src/guards/auth.guard.ts

Example: Auth Guard

// src/guards/auth.guard.ts

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Observable } from 'rxjs';
import { AuthService } from '../auth/auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) {
      throw new UnauthorizedException('Token not provided');
    }

    return this.validateToken(token);
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const authHeader = request.headers['authorization'];
    if (!authHeader) return undefined;
    const [, token] = authHeader.split(' ');
    return token;
  }

  private async validateToken(token: string): Promise<boolean> {
    const isValid = await this.authService.verifyToken(token);
    if (!isValid) {
      throw new UnauthorizedException('Invalid token');
    }
    return true;
  }
}

Explanation:

  • @Injectable(): Marks the class as a provider.

  • CanActivate: Interface that requires the canActivate method.

  • canActivate Method: Determines whether the request should proceed. It extracts the token from the request header and validates it using an AuthService.

  • AuthService: A hypothetical service responsible for token verification. Ensure you have this service implemented.

Step 2: Apply Guards to Controllers and Routes

Guards can be applied at various levels: method, controller, or globally.

Applying Guard to a Controller:

// src/users/users.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { AuthGuard } from '../guards/auth.guard';

@UseGuards(AuthGuard)
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}

Applying Guard to a Specific Route:

// src/users/users.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { AuthGuard } from '../guards/auth.guard';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    return this.usersService.findAll();
  }
}

Explanation:

  • @UseGuards(AuthGuard): Applies the AuthGuard to the controller or specific route, ensuring that only authenticated requests can access the endpoint.

Step 3: Global Guards

To apply a Guard globally across all routes and controllers, configure it in the main.ts file.

Example: Applying Auth Guard Globally

// src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './guards/auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalGuards(app.get(AuthGuard));

  await app.listen(3000);
}
bootstrap();

Explanation:

  • useGlobalGuards: Applies the specified Guard to all routes within the application.

  • app.get(AuthGuard): Retrieves an instance of the AuthGuard from the application's dependency injection container.


Using Middlewares and Guards Together

Middlewares and Guards can complement each other to provide comprehensive request processing and security.

Example: Combining Logger Middleware and Auth Guard

  1. Logger Middleware: Logs every incoming request.

  2. Auth Guard: Ensures that only authenticated requests proceed to the route handler.

Implementation:

// src/app.module.ts

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { LoggerMiddleware } from './middlewares/logger.middleware';

@Module({
  imports: [UsersModule],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}
// src/users/users.controller.ts

import { Controller, Get, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { AuthGuard } from '../guards/auth.guard';

@UseGuards(AuthGuard)
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll() {
    return this.usersService.findAll();
  }
}

Explanation:

  • Middleware: The LoggerMiddleware logs all incoming requests to any route (path: '*').

  • Guard: The AuthGuard protects the users routes, ensuring that only authenticated requests can access them.

This setup ensures that every request is logged and only authenticated requests reach the route handlers.

PreviousStructureNextCore Modules

Last updated 8 months ago

🎗️