Раскройте некоторые более сложные темы и приведите примеры. Мы предполагаем, что хорошо разбираемся в основах 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, что позволит вам работать с конкретными вариантами использования и повысить удобство работы для ваших пользователей.