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.
- Defined with the
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 {}
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) {}
}
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';
}
}
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;
}
}
1.5 Middleware
- Functions that can manipulate the request/response before reaching the controller.
- Use
@Injectable()
andapp.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();
}
}
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() })));
}
}
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
orTransient
: Custom behavior for request-specific providers.
-
Custom Provider Example:
const myProvider = {
provide: "CUSTOM_TOKEN",
useValue: { key: "value" },
};
@Module({
providers: [myProvider],
})
export class AppModule {}
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!");
}
}
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;
}
}
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;
}
}
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,
});
}
}
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);
}
}
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);
}
}
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 {}
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 {}
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
- Modular Design: Split features into modules for scalability.
- SOLID Principles: Use DI to follow single responsibility and open/closed principles.
- DTOs: Define input/output structures to maintain type safety.
- 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
Top comments (0)