GraphQL & ORM

오픈소스·2024년 9월 21일
0
post-thumbnail

https://product.kyobobook.co.kr/detail/S000200019862, P43

TypeORM과 코드 주도 접근 방식의 구현 도구를 제공하는 TypeGraphQL의 통합성은 매우 좋습니다. 두 라이브러리는 동일한 형태의 문법을 제공하므로, 함께 사용 시 적은 코드로 여로 많은 기능을 함께 구현하고 관리할 수 있습니다. (물론 이는 하나의 클래스가 여러 책임을 가지는 형태가 되므로 권장되지는 않습니다만, 이 책에서는 생산성 향상을 위해 이 방향을 소개합니다.)

TypeGraphQL을 사용해 오브젝트를 구성했던 Recipe 클래스에 몇 가지 다른 데토레이터를 추가하는 것만으로 데이터베이스 모델 선언이 완료됩니다.

import {
  Entity, Column, PrimaryGenertedColumn, BaseEntity
} from 'typeorm';
import { ObjectType, Field } from 'type-graphql';

@ObjectType()
@Entity()
class Recipe extends BaseEntity {
  @Field(type => Int)
  @PrimaryColumn()
  id: number;
  
  @Field()
  @Column()
  title: string;
  
  @Field({ nullable: true })
  @Column()
  description?: string;
}

이렇게 GraphQL 타입 정의와 DB 모델 정의를 하나의 파일에서 관리하는 것은 효율적입니다. DB나 GraphQL 타입의 변화가 필요할 때, 각각의 변경 사항을 한 번에 적용할 수 있습니다. 또한 데이터베이스에는 필요한 필드지만 GraphQL API로 외부에 노출될 필요가 없는 경우, 해당 필드에 데이터베이스 컬럼을 의미하는 @Column() 데코레이터는 명시한 채, GraphQL 필드를 의미하는 @Field() 데코레이터는 작성하지 않는 방식으로 구성할 수 있습니다.

@ObjectType()
@Entity()
class Recipe extens BaseEntity {
  @Field(type => Int)
  @PrimaryColumn()
  id: number;
  
  @Field()
  @Column()
  title: string;
  
  @Field({ nullable: true })
  @Column()
  description?: string;
  
  // DB 컬럼 O, GraphQL 필드 X
  @Column()
  someSecretField: string;
}

이제 선언된 DB 모델을 통해, Resolver 클래스에서 곧바로 데이터베이스 오브젝트에 접근할 수 있습니다.

import { Resolver, Query, Mutation } from 'type-graphql';
import { Recipe } from './recipe';

@Resolver(Recipe)
class RecipeResolver {
  @Query(return => Recipe)
  async recipe(@Arg("id") id: number) {
    const recipe = await this.Recipe.findOne(id);
    return recipe;
  }
  
  @Mutation(return => Recipe)
  addRecipe(
    @Arg("newRecipeData") newRecipeData: NewRecipeInput,
  ): Promise<Recipe> {
    return this.Recipe.save({ data: newRecipeData });
  }
}

이전에 DTO layer와 Entity layer를 서로 유도할 수 있을까 고민한 적이 있는데,
https://velog.io/@youngkiu/NestJS-Mapped-types
GraphQL에서 자연스럽게 통합된 솔루션을 제공하고 있다.

0개의 댓글