1. resolver 이동 및 수정
index.ts에 있던 resolver를 옮겨볼게요.
import {Mutation, Query, Resolver} from "type-graphql";
@Resolver()
export class RegisterResolver {
// gql은 종종 쿼리나 스키마가 없을 때 까다로워지는 경우가 있어 아래는 사용은 안하지만 남겨놓는다.
@Query(() => String, {name: 'helloWorld'})
async hello() {
return "hello world!";
}
@Mutation(() => String)
async register() {
return "hello world!";
}
}
서버를 실행하고 스키마를 확인하면 정상적으로 mutation이 만들어진 걸 볼 수 있습니다.
2. @Arg 애너테이션으로 인자 추가
import {Arg, Mutation, Query, Resolver} from "type-graphql";
@Resolver()
export class RegisterResolver {
@Query(() => String, {name: 'helloWorld'})
async hello() {
return "hello world!";
}
@Mutation(() => String)
// @Arg('argument 이름 즉, graphql 스키마에서의 이름') 변수 이름: 타입
async register(@Arg('firstName') firstName: string) {
return firstName;
}
}
argument를 받도록 설정해봅시다. @Arg 애너테이션 안에 graphql 스키마에서의 이름과 실제 함수가 사용할 변수 이름과 타입을 명시해줍니다.
스키마도 그대로 변경된 것을 볼 수 있어요.
3. resolver 로직 추가
import {Arg, Mutation, Query, Resolver} from "type-graphql";
import * as bcrypt from 'bcryptjs';
import {User} from "../../entity/User";
@Resolver()
export class RegisterResolver {
@Query(() => String, {name: 'helloWorld'})
async hello() {
return "hello world!";
}
@Mutation(() => User) // 여기서의 타입은 스키마에서의 타입이다.
async register(
@Arg("firstName") firstName: string,
@Arg("lastName") lastName: string,
@Arg("email") email: string,
@Arg("password") password: string
): Promise<User> { // 함수의 리턴 타입일 뿐 스키마에 영향을 주는 것은 아니다.
const hashedPassword = await bcrypt.hash(password, 12);
const user = await User.create({
firstName,
lastName,
email,
password: hashedPassword
}).save();
return user;
}
}
이제 entity에 정의해두었던 내용을 인자로 다 넣어주고, 비밀번호를 암호화한 뒤 새로운 유저를 생성합니다.
이때 리턴값은 Promise<User> 타입이 되는데요, 이 값은 함수가 리턴하는 타입일 뿐, 스키마와 관련있는 것은 아닙니다. 스키마에서의 타입은 @Mutation 애너테이션에 쓰여있는 User예요!
하지만 여기서 실행을 하면,
이렇게 와장창 에러가 뜰거예요 T.T @Mutation에 정의한 User 타입을 사용하려면 entity에도 설정을 해줘야 하거든요.
4. entity에 ObjectType 설정
import {BaseEntity, Column, Entity, PrimaryGeneratedColumn} from "typeorm";
import {Field, ObjectType} from "type-graphql";
// Register.ts의 함수에서 타입으로 사용하기 위해 ObjectType으로 지정해준다.
@ObjectType()
@Entity()
export class User extends BaseEntity {
// 쿼리에 허용할 필드에 @Field 애너테이션을 붙인다.
@Field()
@PrimaryGeneratedColumn()
id: number;
@Field()
@Column()
firstName: string;
@Field()
@Column()
lastName: string;
@Field()
@Column("text", {unique: true})
email: string;
// password는 쿼리로 노출되면 안되므로 @Field 애너테이션을 붙이지 않는다.
@Column()
password: string;
}
먼저 @ObjectType 애너테이션을 붙여준 뒤, 쿼리에 허용할 필드에 @Field 애너테이션을 붙여주면 됩니다.
5. ID 타입 설정
보통 graphql은 typescript의 타입으로 선언된 타입을 보고 타입을 인식합니다. 하지만 모든 것을 다 처리할 수 있는 건 아니에요.
예를 들어 id 필드의 경우 number로 지정되어 있는데요, 이게 integer인지 double인지 확실하지가 않잖아요?! 그래서 type-graphql이 지원하는 별도의 타입으로 지정해줄 수 있습니다.
@Field(() => ID)
@PrimaryGeneratedColumn()
id: number;
바로 이렇게요!
변경된 스키마를 확인해보세요. User에서 password는 제외된 걸 볼 수 있죠? 데이터베이스 필드는 맞지만 graphql 필드로는 지정되지 않은 것이죠!
6. name 필드에 graphql 타입 적용
graphql 타입만 사용해서 필드 타입을 지정할 수도 있습니다.
@Field()
name: string;
먼저 name을 entity에 추가해준 뒤에
@Resolver(User) // FieldResolver에서 사용할 타입을 넘긴다.
export class RegisterResolver {
@Query(() => String, {name: 'helloWorld'})
async hello() {
return "hello world!";
}
@FieldResolver()
// 최상단의 @Resolver에서 User를 넘기고 있으므로 User를 사용할 수 있게 되었다.
// @Root 애너테이션을 이용해 parent에 User 타입을 지정한다.
async name(@Root() parent: User) {
return `${parent.firstName} ${parent.lastName}`;
}
@Mutation(() => User)
async register(
...
}
}
@Resolver에 User 타입을 넣어주면 사용할 수 있게 되는데요, 이를 위해 name에 @FieldResolver를 붙이고 @Root 애너테이션을 붙입니다.
@Root 애너테이션을 붙일 때는 parent라는 이름을 사용해요. parent를 User 타입으로 지정해주면 User에 있는 내용을 그대로 불러오게 됩니다.
맨 아래에 name이 추가된 것을 보실 수 있습니다!
6. 데모
register에 필요한 인자와 받고 싶은 결과값을 보내면, 정상적으로 처리된 걸 볼 수 있어요.
entity에 email 필드를 추가할 때 unique로 지정했던 것, 기억 나시나요? 이렇게 같은 email로 다시 등록하면 같은 내용이라면서 추가에 실패하고 에러 메시지가 뜨게 됩니다 :)
'개발새발 개발자 > 기타' 카테고리의 다른 글
휴면 처리를 하시겠읍니까, 휴먼? - 2 (0) | 2023.08.05 |
---|---|
휴면 처리를 하시겠읍니까, 휴먼? - 1 (0) | 2023.08.05 |
[TypeGraphQL] 설치 및 resolver, entity 생성하기 (0) | 2020.04.08 |
[AWS] 특정 IP만 접속할 수 있게 설정하기 (0) | 2019.10.07 |
[AWS] 만들어놓은 서버에 접속하기 (Mac OS) (8) | 2019.10.07 |