DEV Community

Cover image for NestJs Backend Overview
Taki089.Dang
Taki089.Dang

Posted on

4

NestJs Backend Overview

NestJS is a powerful and versatile framework for building scalable and maintainable server-side applications. It is built with TypeScript, leverages strong typing, and combines the best features of Object-Oriented Programming (OOP), Functional Programming (FP), and Reactive Programming (RP). Here's a deep dive into everything you need to know about NestJS for backend development:


1. Core Concepts of NestJS

1.1 Modularity

  • Modules: The fundamental building blocks of a NestJS app. Every application has at least one root module (AppModule), and additional modules can be created to organize features.
    • Defined with the @Module() decorator.
    • Enables separation of concerns and scalability by encapsulating services, controllers, and providers.

Example:

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  imports: [],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}
Enter fullscreen mode Exit fullscreen mode

1.2 Dependency Injection (DI)

  • NestJS heavily relies on DI for managing dependencies.
  • Providers are registered in the module and can be injected wherever needed.
  • Promotes clean, testable, and maintainable code.

Example:

@Injectable()
export class UsersService {
  constructor(private readonly httpClient: HttpService) {}
}
Enter fullscreen mode Exit fullscreen mode

1.3 Controllers

  • Handle incoming requests and send responses.
  • Defined with the @Controller() decorator.
  • Use decorators like @Get(), @Post(), etc., to define routes.

Example:

@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return 'This will return all users';
  }
}
Enter fullscreen mode Exit fullscreen mode

1.4 Services

  • Encapsulate business logic and data manipulation.
  • Defined as @Injectable() to be injected into controllers or other services.

Example:

@Injectable()
export class UsersService {
  private users = [{ id: 1, name: 'John Doe' }];

  findAll() {
    return this.users;
  }
}
Enter fullscreen mode Exit fullscreen mode

1.5 Middleware

  • Functions that can manipulate the request/response before reaching the controller.
  • Use @Injectable() and app.use() for implementation.

Example:

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

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    console.log('Request logged:', req.method, req.url);
    next();
  }
}
Enter fullscreen mode Exit fullscreen mode

1.6 Interceptors

  • Transform data before sending it to the client or after receiving a request.
  • Implement NestInterceptor and use @UseInterceptors().

Example:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(map(data => ({ data, timestamp: new Date().toISOString() })));
  }
}
Enter fullscreen mode Exit fullscreen mode

1.7 Providers and Dependency Scope

  • Providers: Anything that can be injected via DI (e.g., services, repositories).
  • Scope:
    • Singleton (default): One instance across the app.
    • Request or Transient: Custom behavior for request-specific providers.

Custom Provider Example:

const myProvider = {
  provide: "CUSTOM_TOKEN",
  useValue: { key: "value" },
};

@Module({
  providers: [myProvider],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

1.8 Lifecycles and Hooks

NestJS provides lifecycle hooks for components like modules and services:

  • OnModuleInit: Triggered when a module is initialized.
  • OnApplicationBootstrap: Called when the app starts.

Example:

@Injectable()
export class AppService implements OnModuleInit {
  onModuleInit() {
    console.log("Module initialized!");
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Advanced Features of NestJS

2.1 Validation with Pipes

  • Pipes are used for validation and transformation.
  • NestJS provides a built-in ValidationPipe to validate incoming requests.

Example:

import { IsString } from 'class-validator';

export class CreateUserDto {
  @IsString()
  readonly name: string;
}

@Controller('users')
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return createUserDto;
  }
}
Enter fullscreen mode Exit fullscreen mode

2.2 Guards

  • Used for authorization logic.
  • Implement CanActivate and use @UseGuards().

Example:

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return request.headers['authorization'] ? true : false;
  }
}
Enter fullscreen mode Exit fullscreen mode

2.3 Filters (Exception Handling)

  • Handle errors and exceptions globally or locally.
  • Use @Catch() to create custom exception filters.

Example:

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

2.4 Events with Event Emitters

  • Built-in event emitter for pub/sub patterns.

Example:

@Injectable()
export class NotificationService {
  constructor(private eventEmitter: EventEmitter2) {}

  sendNotification(data: string) {
    this.eventEmitter.emit('notification', data);
  }
}
Enter fullscreen mode Exit fullscreen mode

2.5 File Uploads

  • Use @nestjs/platform-express for file uploads.

Example:

@Controller('files')
export class FilesController {
  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    console.log(file);
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Integration with External Libraries

3.1 MongoDB

  • Integrate using @nestjs/mongoose.

Example:

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/nest'),
    MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]),
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

3.2 Authentication (Passport.js & JWT)

  • Use @nestjs/passport and @nestjs/jwt for authentication.

Example:

import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [JwtModule.register({ secret: 'secretKey' })],
})
export class AuthModule {}
Enter fullscreen mode Exit fullscreen mode

5. Deployment Best Practices

  • Use environment variables for sensitive data (ConfigModule from @nestjs/config).
  • Bundle the app using Webpack or Dockerize it for production.
  • Use PM2 or similar tools to manage processes.


6. Design Patterns

  1. Modular Design: Split features into modules for scalability.
  2. SOLID Principles: Use DI to follow single responsibility and open/closed principles.
  3. DTOs: Define input/output structures to maintain type safety.
  4. Clean Architecture: Separate controllers, services, and repositories.

7. Folder Structure

src/
├── modules/
│   ├── auth/
│   │   ├── auth.controller.ts
│   │   ├── auth.service.ts
│   │   ├── auth.module.ts
│   ├── user/
│       ├── user.controller.ts
│       ├── user.service.ts
│       ├── user.module.ts
├── common/
│   ├── filters/
│   ├── guards/
│   ├── interceptors/
│   ├── pipes/
├── main.ts
├── app.module.ts
Enter fullscreen mode Exit fullscreen mode

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay