Nest에서 graphql을 사용하기 위해 다음 패키지들을 설치해줍니다.
yarn add @nestjs/graphql graphql@^15 apollo-server-express
api별로 resolver(원래 controller)와 service를 만들어 module로 합쳐주고
module을 다시 app.module에 합치는 구조입니다.
기존의 익스프레스 서버에서 graphql을 사용할 때에는 Schema First 방식으로 api문서의 구조를 일일이 적어주어야 했습니다.
Nest에서는 데코레이터를 사용하여 코드에 맞게 자동으로 schema를 생성해줄 수 있습니다.
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { BoardModule } from './apis/board/board.module';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
@Module({
imports: [
BoardModule, //
GraphQLModule.forRoot({
autoSchemaFile: 'src/common/graphql/schema.gql',
}),
],
// controllers: [AppController],
// providers: [AppService],
})
export class AppModule {}
앱모듈에 스키마를 자동으로 생성할 루트를 설정해줍니다.
import { Query, Resolver } from '@nestjs/graphql';
import { BoardService } from './board.service';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
// private readonly public등을 적을경우 this.boardService = boardService 자동셋팅
// 생략시 기본 public, private은 인스턴스 생성후 외부에서 재할당등 수정불가, readonly는 내부에서도 불가
@Query(() => String)
getHello(): string {
return this.boardService.getHello();
}
}
리졸버에서 @Query(() => String)
getHello요청을 받을 때 리턴값으로 String이 간다는것을 명시해주었습니다.
type Query {
getHello: String!
}
자동으로 생성된 스키마 파일에 따라 문서가 설정됩니다.
Nest에서 typeorm을 이용하여 mysql과 연동하기 위해 다음 패키지들을 설치해줍니다.
yarn add @nestjs/typeorm typeorm mysql2
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { BoardModule } from './apis/board/board.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from './apis/board/entities/board.entity';
// import { AppController } from './app.controller';
// import { AppService } from './app.service';
@Module({
imports: [
BoardModule, //
GraphQLModule.forRoot({
autoSchemaFile: 'src/common/graphql/schema.gql',
}),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'mysql',
entities: [Board],
synchronize: true, // 배포시에는 컬럼값수정등을 하게될경우 데이터가 날아가므로 배포환경에서는 false
logging: true,
}),
],
// controllers: [AppController],
// providers: [AppService],
})
export class AppModule {}
entities/board.entity.ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Board {
@PrimaryGeneratedColumn()
number: number;
@Column()
writer: string;
@Column()
title: string;
@Column()
contents: string;
}
참조할 엔티티(원래모델)와 함께 mysql을 연결해주면 typeorm이 엔티티에 맞게 테이블을 생성하고 연동하여 줍니다.
import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
@ObjectType()
export class Board {
@PrimaryGeneratedColumn()
@Field(() => Int)
number: number;
@Column()
@Field(() => String)
writer: string;
@Column()
@Field(() => String)
title: string;
@Column()
@Field(() => String)
contents: string;
}
자동으로 바뀐 스키마
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type Board {
number: Int!
writer: String!
title: String!
contents: String!
}
type Query {
fetchBoards: [Board!]!
}
type Mutation {
createBoard: String!
}
graphql에서 객체타입을 리턴해줄 때, @ObjectType()
과 @Field()
를 사용해서 api문서를 자동생성할 수 있습니다.
엔티티 하나로 Typeorm과 graphql 그리고 typescript까지 객체의 형식을 참조가능합니다.
graphql은 리턴받는 타입과 별개로 입력을 받는 input타입이 있습니다.
dto폴더를 만들고 createBoard.input.ts파일을 생성해주었습니다.
import { Field, InputType } from '@nestjs/graphql';
@InputType()
export class CreateBoardInput {
@Field(() => String)
writer: string;
@Field(() => String)
title: string;
@Field(() => String)
contents: string;
}
그리고 리졸버에서 인풋타입을 사용하여 요청을 받습니다.
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { BoardService } from './board.service';
import { CreateBoardInput } from './dto/createBoard.input';
import { Board } from './entities/board.entity';
@Resolver()
export class BoardResolver {
constructor(private readonly boardService: BoardService) {}
@Query(() => [Board])
fetchBoards(): Board[] {
return this.boardService.findAll();
}
@Mutation(() => String)
createBoard(
@Args('writer') writer: string,
@Args('title') title: string,
@Args('contents') contents: string,
@Args('createBoardInput') createBoardInput: CreateBoardInput,
): string {
return this.boardService.create({
writer,
title,
contents,
createBoardInput,
});
}
}
@Args()로 따로따로 매개변수로 받을수도 있지만 설정해준 인풋타입을 이용해서 한번에 받을 수 있습니다.
저장해주면 스키마도 자동 생성되어 api문서에 반영됩니다.
# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------
type Board {
number: Int!
writer: String!
title: String!
contents: String!
}
type Query {
fetchBoards: [Board!]!
}
type Mutation {
createBoard(createBoardInput: CreateBoardInput!, contents: String!, title: String!, writer: String!): String!
}
input CreateBoardInput {
writer: String!
title: String!
contents: String!
}