controller 단에서 request 들어오는 데이터에 대한 타입을 지정하는 데에 사용
→ 물론 유효성 검사까지 진행 가능
@InputType()
혹은 @ArgsType()
를 사용import { InputType } from '@nestjs/graphql';
Resolver
에서는 @Args("apple")
해당 객체에 대한 이름을 반드시 넣어주어야 한다.Resolver
는 @Args("apple")
명시해준 이름을 통해 하나의 객체가 들어오게 된다.import { ArgsType } from '@nestjs/graphql';
Resolver
에서 @Args()
이름을 넣어줄 필요가 없다.Resolver
는 여러 개의 인자를 받게 된다. ( 단 하나의 객체만 받는 게 아니라는 것 )import { Field } from '@nestjs/graphql';
Field
를 가져와 해당 key값이 어떤 타입인 지를 지정해준다.// 밑의 클래스에 하나의 필드에 대해 정의하기 위한 Interface
@InputType()
class UpdateRestaurantInputType extends PartialType(CreateRestaurantDto) {}
// 실제로 요청들어오는 데이터에 대한 Interface
@ArgsType()
export class UpdateRestaurantDto {
@Field(type => Number)
id: number;
@Field(type => UpdateRestaurantInputType)
data: UpdateRestaurantInputType;
}
Documentation | NestJS - A progressive Node.js framework
Entity
를 통해 DTO를 만들 수 있다.Entity
를 통해 DTO 를 만들 때에는 Nestjs가 제공해주는 Type class를 상속받으면 된다.// 밑의 클래스에 하나의 필드에 대해 정의하기 위한 Interface
@InputType()
class UpdateRestaurantInputType extends PartialType(CreateRestaurantDto) {}
// 실제로 요청들어오는 데이터에 대한 Interface
@ArgsType()
export class UpdateRestaurantDto {
@Field(type => Number)
id: number;
@Field(type => UpdateRestaurantInputType)
data: UpdateRestaurantInputType;
}
Entity
를 이용하지만, 기본적으로 DB Table Schema를 위해서 만드는 위함이 제일 큰 듯 싶다 ( DB Table Schema 위주로 Entity
를 짜는 듯 )Entity
에서 사용 가능하다.import { ObjectType, Field, InputType, ID } from '@nestjs/graphql';
import { IsString, IsBoolean, Length, IsOptional } from 'class-validator';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@InputType({ isAbstract: true })
@ObjectType() // for GraphQL
@Entity() // for TypeORM
export class Restaurant {
@Field(type => ID)
@PrimaryGeneratedColumn()
id: number;
@Field(type => String) // for GraphQL
@Column() // for typeORM
@IsString()
@Length(5)
name: string;
@Field(type => Boolean, { defaultValue: true }) // for GraphQL
@Column({ default: true }) // for typeORM
@IsBoolean()
@IsOptional()
isVegan: boolean;
@Field(type => String) // for GraphQL
@Column() // for typeORM
@IsString()
address: string;
}
undefined
가 들어오게 될텐데 이럴 때 DB의 default로 설정한 값이 들어가게 된다.resolver.ts
파일 하나로 이 모든 걸 해줄 수 있다 → schema.graphql
을 알아서 만들어주기 떄문!@Resolver(of => Restaurant)
export class RestaurantResolver {
// service를 사용하기 위해선 construcor 함수의 매개변수로 넣어주어야 합니다!
// 이후 이 class 내부의 모든 함수에선 this 키워드를 통해 service에 접근이 가능합니다.
constructor(private readonly restaurantService: RestaurantService) {}
@Query(returns => [Restaurant])
restaurants(): Promise<Restaurant[]> {
return this.restaurantService.getAll();
}
@Mutation(returns => Boolean)
async createRestaurant(
@Args('input') createRestaurantDto: CreateRestaurantDto,
): Promise<boolean> {
try {
await this.restaurantService.createRestaurant(createRestaurantDto);
return true;
} catch (error) {
console.log(error);
return false;
}
}
@Mutation(returns => Boolean)
async updateRestaurant(
@Args() updateRestaurantDto: UpdateRestaurantDto,
): Promise<boolean> {
try {
await this.restaurantService.updateRestaurant(updateRestaurantDto);
return true;
} catch (error) {
console.log(error);
return false;
}
}
}
Controller
와 같은 역할Resolver class
에서 Query
혹은 Mutation
을 데코레이터로 지정하여 resolver
를 만들어준다.Query
OR Mutation
에 대한 schema는 자동으로 생성해줌@Resolver(of => Restaurant)
에서 of는 void의 타입 ( 아무런 뜻이 없음 → 가독성을 위해 적는다. ) 또한, return type의 경우 Function
의 타입인데, 해당 함수도 별 다른 의미가 존재하지 않음@Query(returns => [Restaurant])
를 통해 schema를 생성해준다.@Query()
데코레이터를 손 대지 않고, 아래의 함수 return type을 boolean으로 바꿨을 경우에도 schema는 데코레이터에 작성해준 return type으로 생성된다.type Restaurant {
id: ID!
name: String!
isVegan: Boolean!
address: String!
}
type Query{
restaurants => [Restaurant]
}
@Mutation(returns => Boolean)
을 통해 schema를 생성한다.npm i joi
import * as Joi from 'joi'
ConfigModule.forRoot({
isGlobal: true,
envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.test',
// if NODE_ENV is prod, set environement variable elsewhere
ignoreEnvFile: process.env.NODE_ENV === 'prod',
validationSchema: Joi.object({
NODE_ENV: Joi.valid('dev', 'test', 'prod').required(),
TYPEORM_CONNECTION: Joi.string().required(),
TYPEORM_HOST: Joi.string().required(),
TYPEORM_USERNAME: Joi.string().required(),
TYPEORM_PASSWORD: Joi.string().required(),
TYPEORM_DATABASE: Joi.string().required(),
TYPEORM_PORT: Joi.number().required(),
TYPEORM_SYNCHRONIZE: Joi.boolean().required(),
TYPEORM_LOGGING: Joi.boolean().required(),
}),
}),
Service
파일의 클래스 내부에 선언한다.DB Table 을 이용하기 위해선 module
파일에서 해당 Repository를 import 해주어야 한다.
// ~.module.ts
@Module({
**imports: [TypeOrmModule.forFeature([Restaurant])],**
providers: [RestaurantResolver, RestaurantService],
})
이제 Repository를 import 했으니, 이걸 Service
에서 사용할 수 있게 providers
에 넣어준다.
// ~.module.ts
@Module({
imports: [TypeOrmModule.forFeature([Restaurant])],
**providers: [RestaurantResolver, RestaurantService],**
})
export class RestaurantsModule {}
사용하고자 하는 Service의 constructor
함수에서 해당 Repository를 Inject
해주자!
import { Injectable } from '@nestjs/common';
import { Restaurant } from './entities/restaurant.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class RestaurantService {
constructor(
@InjectRepository(Restaurant)
private readonly restaurants: Repository<Restaurant>,
) {}
}
이제 Service
내부의 모든 함수에서 this
키워드를 통해 해당 Repository에 접근이 가능하다.
Resolver
에서 Service
에 접근하기 위해서는 Resolver
의 constructor
함수 매개변수로 해당 Service
를 적어주면 된다.
import { Resolver } from '@nestjs/graphql';
import { Restaurant } from './entities/restaurant.entity';
import { RestaurantService } from './restaurants.service';
@Resolver(of => Restaurant)
export class RestaurantResolver {
constructor(private readonly restaurantService: RestaurantService) {}
}
// just create class not access to db
const user = repository.create();
// same as
const user = new User();
const user = repository.create({
id: 1,
firstName: "Timber",
lastName: "Saw"
});
// same as
const user = new User();
user.firstName = "Timber"; user.lastName = "Saw";
save
함수를 사용해주어야 한다.const user = repository.create({
id: 1,
firstName: "Timber",
lastName: "Saw"
});
await repository.save(user)
// same as
const user = new User();
user.firstName = "Timber"; user.lastName = "Saw";
await repository.save(user)
Nest Documentation link
import { InputType, OmitType } from '@nestjs/graphql';
import { Restaurant } from '../entities/restaurant.entity';
@InputType()
export class CreateRestaurantDto extends OmitType(
Restaurant,
['id'] as const,
InputType,
) {}
ObjectType
이기 때문에, 현재 상속받은 child class는 ObjectType
을 따라가게 된다.InputType
이여야 하기 때문에, InputType
으로 바꿔준 것.import { InputType, OmitType } from '@nestjs/graphql';
import { Restaurant } from '../entities/restaurant.entity';
@InputType()
export class CreateRestaurantDto extends OmitType(
Restaurant,
['id'] as const,
) {}
import { ObjectType, Field, InputType, ID } from '@nestjs/graphql';
import { IsString, IsBoolean, Length, IsOptional } from 'class-validator';
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@InputType({ isAbstract: true })
@ObjectType() // for GraphQL
@Entity() // for TypeORM
export class Restaurant {
@Field(type => ID)
@PrimaryGeneratedColumn()
id: number;
@Field(type => String) // for GraphQL
@Column() // for typeORM
@IsString()
@Length(5)
name: string;
@Field(type => Boolean, { defaultValue: true }) // for GraphQL
@Column({ default: true }) // for typeORM
@IsBoolean()
@IsOptional()
isVegan: boolean;
@Field(type => String) // for GraphQL
@Column() // for typeORM
@IsString()
address: string;
}
InputType
을 넘기지 않아도 된다.@Args
를 쓰는 것보단 한 개의 @Args
만 사용해서 가독성을 높이자.@Args
보단// resolver
@Mutation(returns => Boolean)
async updateRestaurant(
@Args('id') id: number,
@Args('input') data: UpdateRestaurantDto,
): Promise<boolean> {
return true;
}
@Args
만 사용// resolver
@Mutation(returns => Boolean)
async updateRestaurant(
@Args() updateRestaurant: UpdateRestaurantDto,
): Promise<boolean> {
return true;
}
// dto
import { InputType, PartialType, Field, ArgsType } from '@nestjs/graphql';
import { CreateRestaurantDto } from './create-restaurant.dto';
// 밑의 클래스에 하나의 필드에 대해 정의하기 위한 Interface
@InputType()
class UpdateRestaurantInputType extends PartialType(CreateRestaurantDto) {}
// 실제로 요청들어오는 데이터에 대한 Interface
@ArgsType()
export class UpdateRestaurantDto {
@Field(type => Number)
id: number;
@Field(type => UpdateRestaurantInputType)
data: UpdateRestaurantInputType;
}
@InputType 을 사용할 경우, resolver에서 반드시 @Args(' ... ') 이름을 넣어주자!