확장 가능한 일반 Nestjs 컨트롤러 빌드 — 1부

Django 휴식 프레임워크로 작업할 때 ModelViewSet이라는 클래스가 있었습니다. 이 클래스를 상속하고 데이터베이스 모델 이름을 지정하면 모든 CRUD 엔드포인트가 자동으로 생성됩니다. 저는 이 아이디어가 정말 마음에 들었습니다. API에 대한 일관성을 제공하고, 작성해야 하는 코드가 줄어들고, 마침내 비즈니스 로직에 더 집중할 수 있게 해 주기 때문입니다.

이 문서에서는 확장 시 ModelViewSet과 유사한 기능을 제공하는 제네릭 클래스에 대한 구현을 제공합니다.

이를 위해 TypeORM 라이브러리가 모델 라이브러리로 사용됩니다. 예를 들어, 고전적인 기사 모델을 수행하고 모델이 다음과 같이 엔드포인트를 수행하는 방법을 보여줍니다.

@Entity('article')
export class ArticleEntity extends BaseEntity {
  @PrimaryGeneratedColumn({ name: 'article_id' })
  articleId: number;

  @Column()
  title: string;

  @Column()
  body: string;
}


GET 엔드포인트가 있는 이 모델의 컨트롤러는 일반적으로 다음과 같습니다.

@Controller({ path: 'article' })
class ArticleController {
 constructor(
   @InjectRepository(ArticleEntity)
   protected readonly repository: Repository<ArticleEntity>,
  ) {}
  @Get(':id')
  async get(@Param('id', ParseIntPipe) id: number):
  Promise<ArticleEntity> { 
   return this.repository.findOne({ where: { articleId: id } });
  }
}


이제 GET 함수를 사용하여 추상 일반 클래스를 만들어야 합니다. EndPointSet이라고 부르겠습니다. 구현하지 않으면 다음과 같이 표시됩니다.

abstract class EndPointSet<T extends BaseEntity> { 
  protected abstract readonly repository: Repository<T>;
  @Get(':id')
  async get(@Param('id', ParseIntPipe) id: number): Promise<T> {
    // IMPLEMENTATION HERE, KEEP READING ^^
  }
}


하지만 이제 문제가 생겼습니다. 각 모델마다 다른 id 필드 이름이 있으므로 findOne 메서드를 사용할 수 없습니다. 다행스럽게도 Typeform은 이름이 아닌 id 값만 필요한 findOneById 메서드를 제공하므로 메서드 구현은 이다:

return this.repository.findOneById(id);


이제 우리는 다음과 같이 ArticleController뿐만 아니라 모든 컨트롤러에서 EndPointSet을 상속할 수 있습니다.

@Controller({ path: 'article' })
export class ArticleController extends EndPointSet<ArticleEntity> {
  constructor(
  @InjectRepository(ArticleEntity)
     protected readonly repository: Repository<ArticleEntity>,
  ) {super();}
}


다른 엔드포인트는 Get 함수에서 수행한 것과 유사하므로 마지막 클래스는 다음과 같습니다.

abstract class EndPointSet<T extends BaseEntity> {
  protected abstract readonly repository: Repository<T>;
  @Get(':id')
  async get(@Param('id', ParseIntPipe) id: number): Promise<T> {
    return this.repository.findOneById(id);
  }

  @Get('list')
  async list(): Promise<T[]> {
    return this.repository.find();
  }

  @Post()
  async post(@Body() dto: DeepPartial<T>): Promise<T> {
    return this.repository.save(dto);
  }

  @Delete()
  async delete(@Param('id', ParseIntPipe) id: number): Promise<void> {
    this.repository.delete(id);
  }

  @Patch(':id')
  async update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: QueryDeepPartialEntity<T>,
  ): Promise<void> {
    this.repository.update(id, dto);
  }
}




예, 그게 다입니다. 이제 모든 CRUD 끝점을 사용하여 일반 컨트롤러를 만들었습니다. 첫 번째 부분은 여기까지입니다. 다음 부분에서는 페이지 매김, 정렬, 필터 등을 수행하기 위해 목록 기능에 대해 더 많은 작업을 수행할 것입니다.

좋은 웹페이지 즐겨찾기