Раскройте некоторые более сложные темы и приведите примеры. Мы предполагаем, что хорошо разбираемся в основах NestJS и TypeScript.
Пользовательские декораторы:
NestJS позволяет создавать собственные декораторы для различных вариантов использования, таких как извлечение данных из объектов запроса или проверка входных данных.
Пример: создание пользовательского декоратора CurrentUser
import { createParamDecorator, ExecutionContext } from '@nestjs/common'; export const CurrentUser = createParamDecorator( (data: unknown, ctx: ExecutionContext) => { const request = ctx.switchToHttp().getRequest(); return request.user; }, );
Использование в контроллере:
import { CurrentUser } from './current-user.decorator'; @Controller('profile') export class ProfileController { @Get() getProfile(@CurrentUser() user: User) { return user; } }
Пользовательские фильтры исключений:
Фильтры исключений позволяют перехватывать и обрабатывать исключения централизованно, обеспечивая согласованные ответы на ошибки.
Пример. Создание пользовательского фильтра HttpExceptionFilter
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, } from '@nestjs/common'; import { Response } from 'express'; @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(); const message = exception.message; response.status(status).json({ statusCode: status, message, }); } }
Использование в контроллере:
import { HttpExceptionFilter } from './http-exception.filter'; @Controller('users') @UseFilters(HttpExceptionFilter) export class UsersController { // ... }
Пользовательские перехватчики:
Перехватчики предоставляют способ изменить ответ, возвращаемый обработчиком запроса, что позволяет использовать такие варианты использования, как преобразование ответов или ведение журнала производительности.
Пример: создание пользовательского TransformInterceptor
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable() export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> { intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> { return next.handle().pipe(map(data => ({ data }))); } }
Использование в контроллере:
import { TransformInterceptor } from './transform.interceptor'; @Controller('posts') @UseInterceptors(TransformInterceptor) export class PostsController { // ... }
Пользовательские охранники:
Охранники отвечают за определение того, следует ли обрабатывать запрос на основе определенных условий, таких как контроль доступа или статус аутентификации.
Пример: создание пользовательского «RolesGuard»
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const roles = this.reflector.get<string[]>('roles', context.getHandler()); if (!roles) { return true; } const request = context.switchToHttp().getRequest(); const user = request.user; return this.matchRoles(roles, user.roles); } private matchRoles(allowedRoles: string[], userRoles: string[]): boolean { return userRoles.some(role => allowedRoles.includes(role)); } }
Использование в контроллере:
import { Roles } from './roles.decorator'; import { RolesGuard } from './roles.guard'; @Controller('admin') @UseGuards(RolesGuard) export class AdminController { @Get() @Roles('admin') getAdminDashboard() { // ... } }
Пользовательские трубы:
Каналы позволяют проверять или преобразовывать входные данные до того, как они попадут в обработчик маршрута.
Пример: создание пользовательского «ParseIntPipe»
import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common'; @Injectable() export class ParseIntPipe implements PipeTransform<string, number> { transform(value: string): number { const val = parseInt(value, 10); if (isNaN(val)) { throw new BadRequestException('Validation failed'); } return val; } }
Использование в контроллере:
import { ParseIntPipe } from './parse-int.pipe'; @Controller('users') export class UsersController { @Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { // ... } }
Гибридное приложение (микросервисы):
NestJS позволяет создавать гибридные приложения, сочетающие RESTful API с шаблонами микросервисов. В одном приложении можно определить как контроллеры HTTP, так и шаблоны сообщений микрослужб.
Пример: создание гибридного приложения
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Transport } from '@nestjs/microservices'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.connectMicroservice({ transport: Transport.TCP, options: { port: 3001, }, }); await app.startAllMicroservicesAsync(); await app.listen(3000); } bootstrap();
Интеграция с GraphQL:
NestJS поддерживает GraphQL «из коробки», предоставляя гибкий способ запроса и обработки данных. С помощью GraphQL вы можете определить форму ответов вашего API, уменьшив количество избыточной и недостаточной выборки.
Пример: настройка GraphQL с NestJS
Установите необходимые пакеты:
npm install @nestjs/graphql graphql-tools graphql apollo-server-express
Создайте простую схему:
type Post { id: ID! title: String! content: String! } type Query { posts: [Post!]! } type Mutation { createPost(title: String!, content: String!): Post! }
Настройте GraphQLModule
в своем AppModule
:
import { GraphQLModule } from '@nestjs/graphql'; import { join } from 'path'; @Module({ imports: [ GraphQLModule.forRoot({ autoSchemaFile: join(process.cwd(), 'src/schema.gql'), }), ], }) export class AppModule {}
Создайте объект Post
и объект PostResolver
:
// post.entity.ts @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; @Column() content: string; } // post.resolver.ts import { Resolver, Query, Mutation, Args } from '@nestjs/graphql'; import { Post } from './post.entity'; import { PostService } from './post.service'; @Resolver(of => Post) export class PostResolver { constructor(private postService: PostService) {} @Query(returns => [Post]) async posts() { return this.postService.findAll(); } @Mutation(returns => Post) async createPost( @Args('title') title: string, @Args('content') content: string, ) { return this.postService.create({ title, content }); } }
Благодаря интеграции GraphQL вы можете предложить своим клиентам более гибкий и эффективный API. Это позволяет создавать приложения, которые могут развиваться в соответствии с вашими меняющимися требованиями к данным, сводя к минимуму необходимость внесения критических изменений в API.
Расширенное промежуточное ПО и перехватчики:
Как разработчик, понимание того, как создавать расширенное промежуточное ПО и перехватчики, важно для оптимизации и улучшения способа обработки запросов вашим приложением.
Пример: ПО промежуточного слоя для ведения журналов
Создайте пользовательское промежуточное ПО, которое регистрирует входящие запросы:
// logger.middleware.ts import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response } from 'express'; @Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: Function) { console.log(`[${req.method}] ${req.url}`); next(); } }
Примените промежуточное ПО в своем модуле:
// app.module.ts import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common'; import { LoggerMiddleware } from './logger.middleware'; @Module({ // ... }) export class AppModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggerMiddleware) .forRoutes({ path: '*', method: RequestMethod.ALL }); } }
Пример: TransformInterceptor
Перехватчик, преобразующий ответы в следующий формат: { data: response }
:
// transform.interceptor.ts 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 }))); } }
Примените перехватчик глобально в вашем основном файле:
// main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { TransformInterceptor } from './transform.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalInterceptors(new TransformInterceptor()); await app.listen(3000); } bootstrap();
Используя расширенное промежуточное ПО и перехватчики, вы можете модифицировать и оптимизировать обработку запросов и ответов. Это позволяет создать согласованный и оптимизированный API.
Расширенная интеграция микросервисов:
Как старший разработчик NestJS, понимание расширенной интеграции микросервисов имеет решающее значение для создания масштабируемых и отказоустойчивых приложений. Это включает в себя знание шаблонов связи, транспортных стратегий и сериализации сообщений.
Пример: микросервисы с RabbitMQ
Чтобы создать микросервис, использующий RabbitMQ, установите необходимые пакеты:
npm install --save @nestjs/microservices amqplib
Определите простую математическую службу:
// math.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class MathService { add(data: number[]): number { return data.reduce((a, b) => a + b); } }
Реализуйте контроллер микросервиса, использующий математический сервис:
// app.controller.ts import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { MathService } from './math.service'; @Controller() export class AppController { constructor(private readonly mathService: MathService) {} @MessagePattern('add') add(data: number[]): number { return this.mathService.add(data); } }
Создайте микросервис, который слушает RabbitMQ:
// main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { MicroserviceOptions, Transport } from '@nestjs/microservices'; async function bootstrap() { const app = await NestFactory.createMicroservice<MicroserviceOptions>( AppModule, { transport: Transport.RMQ, options: { urls: ['amqp://localhost:5672'], queue: 'nestjs_microservice_queue', queueOptions: { durable: false }, }, }, ); app.listen(() => console.log('Microservice is listening')); } bootstrap();
Создайте клиент, который отправляет сообщения микросервису:
// main-client.ts import { NestFactory } from '@nestjs/core'; import { ClientProxyFactory, Transport } from '@nestjs/microservices';
В этом примере показано, как создать микросервис, используя RabbitMQ в качестве транспортного уровня. Интеграция микросервисов в NestJS требует понимания различных транспортных и коммуникационных шаблонов для создания устойчивых и масштабируемых приложений.
Используя эти передовые концепции NestJS, разработчики могут создавать более безопасные, эффективные и удобные в сопровождении приложения. В этих примерах показано, как применить настройки к вашему приложению NestJS, что позволит вам работать с конкретными вариантами использования и повысить удобство работы для ваших пользователей.