NestJS에서의 GraphQL

LeeJaeHoon·2021년 12월 4일
0
post-thumbnail

Installation

npm i @nestjs/graphql graphql@^15 apollo-server-express

Overview

Nest는 GraphQL 애플리케이션을 빌드하는 두가지 방법, 즉 코드 우선(code first)스키마 우선(schema first) 방법을 제공합니다.

저는 코드 우선 방식으로 GraphQL을 빌드하고자 합니다.

코드 우선 방식의 이점

코드 우선 방식에서는 데코레이터와 Typescript 클래스를 사용하여 해당 GrapQL 스키마를 자동 생성합니다. 이방법은 Typescript로만 작업하고 언어 구문 간의 컨텍스트 전환을 피하려는 경우 유용합니다.

적용

app.module.ts

코드 우선 접근 방식을 사용하려면 먼저 옵션 객체에 autoSchemaFile 속성을 추가합니다.

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { RestaurantsModule } from './restaurants/restaurants.module';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true, //메모리에 저장
    }),
    RestaurantsModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

forRoot() 메소드는 옵션 객체를 인수로받습니다. 이러한 옵션은 기본 Apollo 인스턴스로 전달됩니다.

autoSchemaFile 을 true로 해주면 자동 생선된 스키마를 메모리에 저장할 수 있습니다.

restaurants.Module.ts

RestaurantsModule의 providers로 RestaurantResolver을 넣어 줍니다.

import { Module } from '@nestjs/common';
import { RestaurantResolver } from './restaurants.resolver';

@Module({
  providers: [RestaurantResolver],
})
export class RestaurantsModule {}

restaurant.entity.ts

entities폴더를 생성해주고 해당 폴더에서 restaurant.entity.ts파일을 만들어 줍니다.

import { Field, ObjectType } from '@nestjs/graphql';

@ObjectType() //Restaurant을 위한 Object type을 만들어 준다.
export class Restaurant {
  @Field((is) => String) //@Field의 첫번째 argument로는 returnTypeFunction이 와야한다.
  name: string;
  @Field((type) => Boolean, { nullable: true })
  isGood?: boolean;
}

@Field() 데코레이터는 선택적 유형 함수 (예: type => Int)와 선택적으로 옵션 객체를 허용합니다.

옵션 객체는 다음과 같이 키/값 쌍을 가질 수 있습니다.

  • nullable: 필드가 nullable인지 여부를 지정합니다 (SDL에서 각 필드는 기본적으로 nullable이 아님) boolean
  • description : 필드 설명을 설정합니다; string
  • deprecationReason : 필드를 사용되지 않음으로 표시 string

restaurants.resolver.ts

이 시점에서 데이터 그래프에 존재할 수 있는 객체 (유형 정의)를 정의했지만 클라이언트는 아직 이러한 객체와 상호 작용할 수 있는 방법이 없습니다. 이를 해결하려면 리졸버 클래스를 만들어야 합니다.

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { CreateRestaurantDto } from './dtos/create-restaurant.dto';
import { Restaurant } from './entities/restaurant.entity';

@Resolver((of) => Restaurant) //Restaurant의 resolver이라는 것
export class RestaurantResolver {
  @Query((returns) => [Restaurant])
  restaurants(@Args('veganOnly') veganOnly: boolean): Restaurant[] {
    return [];
  }
}

위와 같이 작성시 SDL에서 GraphQL schema의 다음 부분을 생성합니다.

type Restaurant {
  name: String!
  isVegan: Boolean
  address: String!
  ownerName: String!
}

type Query {
  restaurants(veganOnly: Boolean!): [Restaurant!]!
}

위의 코드에서 알 수 있듯이 @Args('veganOnly') veganOnly: boolean을 통해 @Query에 인자를 넣을 수 있습니다.

다른 예시를 살펴볼까요?? Query의 인자로 여러 값이 필요할 때는 어떻게 해야할까요

createRestaurant handler method를 만들고 인자로 name, isVegan, address, ownerName이 필요하다고 합시다.

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { Restaurant } from './entities/restaurant.entity';

@Resolver((of) => Restaurant) //Restaurant의 resolver이라는 것
export class RestaurantResolver {
  @Query((returns) => [Restaurant])
  restaurants(@Args('veganOnly') veganOnly: boolean): Restaurant[] {
    return [];
  }
  @Mutation((returns) => Boolean)
  createRestaurant(
    @Args('name') name: string,
    @Args('isVegan') isVegan: boolean,
    @Args('address') address: string,
    @Args('ownerName') ownerName: string,
  ): boolean {
    return true;
  }
}

위의 방식으로 할 수 있을 겁니다. 하지만 코드양이 너무 길죠? 이럴때 @ArgsType를 사용하면 코드를 줄일 수 있습니다.

ArgsType

먼저 dtos폴더를 만들고 dtos폴더에 create-restaurant.dto.ts파일을 만듭시다.

여기서 class-validator를 통해 제한을 줄 수 도 있습니다.

import { ArgsType, Field } from '@nestjs/graphql';
import { IsBoolean, IsString, Length } from 'class-validator';

@ArgsType()
export class CreateRestaurantDto {
  @Field((type) => String)
  @IsString()
  @Length(5, 10)
  name: string;

  @Field((type) => Boolean)
  @IsBoolean()
  isVegan: Boolean;

  @Field((type) => String)
  @IsString()
  address: string;

  @Field((type) => String)
  @IsString()
  ownerName: string;
}

위의 코드로 만들어진 CreateRestaurantDto를 이용해 코드양을 줄일 수 있습니다.

restaurants.resolver.ts

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { CreateRestaurantDto } from './dtos/create-restaurant.dto';
import { Restaurant } from './entities/restaurant.entity';

@Resolver((of) => Restaurant) //Restaurant의 resolver이라는 것
export class RestaurantResolver {
  @Query((returns) => [Restaurant])
  restaurants(@Args('veganOnly') veganOnly: boolean): Restaurant[] {
    return [];
  }
  @Mutation((returns) => Boolean)
  createRestaurant(@Args() createRestaurantDto: CreateRestaurantDto): boolean {
    return true;
  }
}

위와 같이 작성시 SDL에서 GraphQL schema의 다음 부분을 생성합니다.

type Restaurant {
  name: String!
  isVegan: Boolean
  address: String!
  ownerName: String!
}

type Query {
  restaurants(veganOnly: Boolean!): [Restaurant!]!
}

type Mutation {
  createRestaurant(
    name: String!
    isVegan: Boolean!
    address: String!
    ownerName: String!
  ): Boolean!
}

0개의 댓글