阿里云主机折上折
  • 微信号
Current Site:Index > NestJS architecture

NestJS architecture

Author:Chuan Chen 阅读数:33365人阅读 分类: Node.js

NestJS is a progressive Node.js framework for building efficient and scalable server-side applications. It adopts a modular design, combining the advantages of object-oriented programming, functional programming, and reactive programming, while defaulting to integration with Express or Fastify under the hood, offering developers flexible choices.

Core Architecture Design

The core architecture of NestJS is built upon several key concepts:

  1. Module System: Applications are organized into modules, with each module encapsulating specific functionality.
  2. Dependency Injection: Loose coupling of components is achieved through decorators.
  3. Controllers: Handle HTTP requests and return responses.
  4. Providers: Injectable components like services and repositories for business logic.
  5. Middleware: Functions that handle the request/response cycle.
  6. Exception Filters: Centralized handling of application exceptions.
  7. Pipes: Data validation and transformation.
  8. Guards: Route access control.
  9. Interceptors: Additional logic before and after method execution.

Module System Explained

Modules are the fundamental organizational units of NestJS applications, defined using the @Module() decorator:

@Module({
  imports: [OtherModule],
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService]
})
export class UserModule {}
  • imports: Import other modules.
  • controllers: Register controllers.
  • providers: Register providers (services).
  • exports: Expose providers for use by other modules.

Dependency Injection Mechanism

NestJS's dependency injection system is a core strength of its architecture, implemented via constructor injection:

@Injectable()
export class UserService {
  constructor(private readonly repository: UserRepository) {}
  
  async findAll(): Promise<User[]> {
    return this.repository.find();
  }
}

Using services in controllers:

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}
  
  @Get()
  async findAll(): Promise<User[]> {
    return this.userService.findAll();
  }
}

Controllers and Routing

Controllers handle incoming requests and return responses:

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `This action returns a #${id} cat`;
  }

  @Post()
  @HttpCode(204)
  create() {
    return 'This action adds a new cat';
  }
}

Providers and Services

Providers are the primary carriers of business logic in NestJS:

@Injectable()
export class NotificationService {
  private readonly clients: Client[] = [];

  addClient(client: Client) {
    this.clients.push(client);
  }

  sendAll(message: string) {
    this.clients.forEach(client => client.send(message));
  }
}

Middleware System

Middleware can process requests and responses:

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`Request... ${req.method} ${req.path}`);
    next();
  }
}

Applying middleware in a module:

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');
  }
}

Exception Handling

NestJS provides an exception filter mechanism:

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

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      message: exception.message
    });
  }
}

Using in controllers:

@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
  throw new ForbiddenException();
}

Pipes and Data Validation

Pipes are used for data transformation and validation:

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    if (!value) {
      throw new BadRequestException('No data submitted');
    }
    return value;
  }
}

Using built-in validation pipes:

@Post()
async create(
  @Body(new ValidationPipe()) createUserDto: CreateUserDto
) {
  // ...
}

Guards and Authentication

Guards are used for route access control:

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}

Using in controllers:

@UseGuards(AuthGuard)
@Controller('profile')
export class ProfileController {}

Interceptors

Interceptors can add logic before and after method execution:

@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`)));
  }
}

Applying interceptors:

@UseInterceptors(LoggingInterceptor)
@Controller('users')
export class UserController {}

Microservices Support

NestJS natively supports microservice architecture:

const app = await NestFactory.createMicroservice<MicroserviceOptions>(
  AppModule,
  {
    transport: Transport.TCP,
    options: { host: 'localhost', port: 3001 }
  }
);
await app.listen();

WebSocket Integration

NestJS provides excellent WebSocket support:

@WebSocketGateway()
export class EventsGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('events')
  handleEvent(client: any, data: string): string {
    return data;
  }
}

Testing Strategy

NestJS offers comprehensive testing tools:

describe('UserController', () => {
  let userController: UserController;
  let userService: UserService;

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      controllers: [UserController],
      providers: [UserService],
    }).compile();

    userService = moduleRef.get<UserService>(UserService);
    userController = moduleRef.get<UserController>(UserController);
  });

  describe('findAll', () => {
    it('should return an array of users', async () => {
      const result = ['test'];
      jest.spyOn(userService, 'findAll').mockImplementation(() => result);
      expect(await userController.findAll()).toBe(result);
    });
  });
});

Configuration Management

NestJS recommends using ConfigModule for configuration management:

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.development.env',
    }),
  ],
})
export class AppModule {}

Using configurations:

@Injectable()
export class DatabaseService {
  constructor(private configService: ConfigService) {
    const dbUser = this.configService.get<string>('DATABASE_USER');
  }
}

Database Integration

NestJS supports various ORM integrations, with TypeORM as an example:

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'test',
      entities: [User],
      synchronize: true,
    }),
    TypeOrmModule.forFeature([User]),
  ],
})
export class AppModule {}

Defining entities:

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  firstName: string;

  @Column()
  lastName: string;
}

Performance Optimization

NestJS applications can be optimized in various ways:

  1. Use FastifyAdapter instead of the default Express.
  2. Implement caching strategies.
  3. Use compression middleware.
  4. Enable cluster mode.
  5. Optimize the dependency injection tree.

Enabling Fastify:

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );
  await app.listen(3000);
}

Deployment Strategies

NestJS applications can be deployed in various environments:

  1. Traditional server deployment.
  2. Containerized deployment (Docker).
  3. Serverless architecture.
  4. Platform as a Service (PaaS).

Docker deployment example:

FROM node:16-alpine

WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

EXPOSE 3000
CMD ["node", "dist/main"]

Ecosystem and Plugins

NestJS has a rich ecosystem:

  1. @nestjs/swagger - API documentation generation.
  2. @nestjs/graphql - GraphQL support.
  3. @nestjs/bull - Queue processing.
  4. @nestjs/terminus - Health checks.
  5. @nestjs/schedule - Task scheduling.

Swagger integration example:

const config = new DocumentBuilder()
  .setTitle('Cats example')
  .setDescription('The cats API description')
  .setVersion('1.0')
  .addTag('cats')
  .build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

Architectural Best Practices

  1. Design modules following the Single Responsibility Principle.
  2. Organize code structure using Domain-Driven Design (DDD).
  3. Maintain clear layer separation (controller-service-repository).
  4. Keep business logic decoupled from the framework.
  5. Write comprehensive unit and integration tests.

Domain-Driven Design example structure:

src/
├── users/
│   ├── entities/
│   ├── repositories/
│   ├── services/
│   ├── controllers/
│   └── users.module.ts
├── products/
│   ├── entities/
│   ├── repositories/
│   ├── services/
│   ├── controllers/
│   └── products.module.ts
└── shared/
    ├── infrastructure/
    └── common/

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:Koa与中间件机制

下一篇:GraphQL实现

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.