Я строю API, используя Nest.js и MySQL. Из-за принципов гибкости и DRY я создаю структуру ООП, которая устанавливает все основные конечные точки CRUD для данного объекта (из TypeORM). Основная цель - избежать написания одинаковых общих методов для разных сущностей.
Чтобы достичь этого, я использую стратегию с Обобщением TypeScript . Мне все еще нужно создать все общие файлы (.controller.ts
, .service.ts
, .module.ts
, .entity.ts
) для каждой сущности, но мне не нужно писать ее методы. Вместо этого я просто расширяю два класса: RestController
и RestService
. Эти классы уже реализуют общие методы, но я должен передать некоторые T типов в качестве параметров, чтобы TypeORM мог внедрить правильный репозиторий для Сервиса.
Проблема: Декоратор @UsePipes
не вызывается, когда я использую его в родительском классе (RestController
), но работает нормально, когда я перезаписываю de RestController создать метод в дочернем классе (SubcategoriesController
).
rest.controller.ts:
import { Get, Post, Body, Param, Put, Delete, UsePipes, ValidationPipe } from '@nestjs/common';
import { RestService } from './rest.service';
import { ObjectLiteral } from 'typeorm';
export abstract class RestController<T, C = T, U = T> {
constructor(protected service: RestService<T, C, U>) {}
@Get()
async index(): Promise<T[]> {
return this.service.getAll();
}
@Post('create')
@UsePipes(ValidationPipe) //HERE!
async create(@Body() data: C): Promise<T> {
return this.service.create(data as C);
}
}
rest.service.ts:
import { Repository, UpdateResult, DeleteResult, Entity, DeepPartial } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
export interface RestClass<T, C = T, U = T> {
// Properties
repository: Repository<T>;
// Default Methods
getAll(): Promise<T[]>;
create(model: T | C | U): Promise<T>;
}
export class RestService<T, C = T, U = T> implements RestClass<T, C, U> {
constructor(
public repository: Repository<T>,
) {}
getAll = async () => {
return await this.repository.find({relations:: this.repository.metadata.ownRelations.map(r => r.propertyName)});
}
create = async (model: C) => {
return await this.repository.save(model as C);
}
}
А вот как я устанавливаю конечные точки реальной сущности, расширяя вышеуказанные классы:
subcategories.controller.ts:
import { Controller, Get, Post, UsePipes, ValidationPipe, Body } from '@nestjs/common';
import { SubcategoriesService } from './subcategories.service';
import { Subcategory } from './subcategory.entity';
import { RestController } from '../rest.controller';
import { CreateSubcategoryDTO } from './dto/createSubcategory.dto';
//NOTE THE TYPE PARAMS IN <>
@Controller('subcategories')
export class SubcategoriesController extends RestController<Subcategory, CreateSubcategoryDTO> {
constructor(public service: SubcategoriesService) {
super(service);
}
}
subcategories.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Subcategory } from './subcategory.entity';
import { Repository } from 'typeorm';
import { RestService } from '../rest.service';
import { CreateSubcategoryDTO } from './dto/createSubcategory.dto';
//NOTE THE TYPE PARAMS IN <>
@Injectable()
export class SubcategoriesService extends RestService<Subcategory, CreateSubcategoryDTO> {
constructor(
@InjectRepository(Subcategory) repository: Repository<Subcategory>,
) {
super(repository);
}
}
createSubcategory.dto.ts
import { IsString, Length, IsInt } from 'class-validator';
export class CreateSubcategoryDTO {
@IsString()
@Length(5, 60)
name: string;
@IsString()
@Length(0, 140)
summary: string;
@Length(0, 140)
icon: string;
@IsInt()
category: number;
}
Вы можете видеть, что родительский класс принимает параметры 3 типов:
T
: сущность
C
: CreateDTO, необязательно
U
: UpdateDTO, необязательно
Приведенный выше код прекрасно создает конечные точки, однако он не проверяет полезную нагрузку в /create
, как ожидалось от ValidationPipe .
Если я перезаписываю метод create в SubcategoriesController и добавляю туда UsePipes, он работает !
Я думаю, что это может быть ошибкой в отношении жизненного цикла Nests, который может не поддерживать использование Pipes в абстрактных классах.
У кого-нибудь есть идея?
P.S . Нет ошибок переноса, предупреждений lint или исключений времени выполнения.