-
login API on nestJS using graphqlProject using Nest.js/E-commerce App 2021. 9. 2. 13:32
user.entity.ts
import { Field, InputType, ObjectType, registerEnumType, } from '@nestjs/graphql'; import { IsBoolean, IsEmail, IsEnum, IsString } from 'class-validator'; import { BeforeInsert, Column, Entity } from 'typeorm'; import * as bcrypt from 'bcrypt'; import { CoreEntity } from 'src/common/entities/common.entity'; import { InternalServerErrorException } from '@nestjs/common'; export enum UserRole { Consumer = 'Consumer', Provider = 'Provider', } enum Language { Korean = 'Korean', English = 'English', } registerEnumType(UserRole, { name: 'UserRole' }); registerEnumType(Language, { name: 'Language' }); @InputType('UserInputType', { isAbstract: true }) @ObjectType() @Entity() export class User extends CoreEntity { @Column({ unique: true }) @Field((type) => String) @IsEmail() email: string; @Column() @Field((type) => String) @IsString() password: string; @Column({ type: 'enum', enum: UserRole, default: UserRole.Consumer }) @Field((type) => UserRole) @IsEnum(UserRole) role: UserRole; @Column({ default: false }) @Field((type) => Boolean) @IsBoolean() verified: boolean; @Column({ default: Language.Korean }) @Field((type) => Language) @IsEnum(Language) language: Language; @Column({ nullable: true }) @Field((type) => String, { nullable: true }) @IsString() bio?: string; }
- @Entity() decorator는 entity를 정의할 수 있게 해준다. 그 안에 email, password 등의 필드들을 정의할 수 있다. TypeORM을 위한 코드다.
ref : https://docs.nestjs.com/techniques/database#repository-pattern - @ObjectType() decorator는 Graphql을 위한 코드다.
ref : https://docs.nestjs.com/graphql/resolvers#object-types - @InputType()는 DTOs를 위한 코드다. mapped types를 위해서 @InputType()을 해줘야 한다. 그리고 InputType에 'UserInputType'이라고 이름을 지정해줘야 한다. 그렇지 않으면 나중에 reloation field가 있을 때 @ObjectType가 충돌하여 error가 발생한다. 그리고 InputType은 DB에 등록되는 것이 아니므로 isAbstract를 설정한다.
ref : https://docs.nestjs.com/graphql/mapped-types#partial
- @Column()은 TypeORM의 Schema로 정의하는 decorator다. @Column()을 하고 밑에 field를 정의하면 db field에 등록된다.
- @Field()는 graphql의 type을 정의한다.
- @IsEmail(), @IsString() 등은 class-validator로 유효성 검사를 한다.
- enum type은 registerEnumType(UserRole, { name: 'UserRole' });를 사용하여서 등록해서 쓸 수 있다.
app.module.ts
UserModule을 @Module({})에 추가해준다.
user.entity.ts에 정의한 User를 TypeOrmModule.forRoot({})에 있는 entities옵션에 추가한다.
users.module.ts
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './entities/user.entity'; import { UsersResolver } from './users.resolver'; import { UsersService } from './users.service'; @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [UsersResolver, UsersService], }) export class UsersModule {}
- imports 옵션은 이 모듈에서 exports할 providers에 필요한 모듈을 import한다.
- providers는 이 모듈에서 쓸 수 있는 것들을 제공한다.
login.dto.ts
import { Field, InputType, ObjectType, PickType } from '@nestjs/graphql'; import { CoreOutput } from 'src/common/dtos/output.dto'; import { User } from '../entities/user.entity'; @InputType() export class LoginInput extends PickType(User, ['email', 'password']) {} @ObjectType() export class LoginOutput extends CoreOutput { @Field((type) => String) token?: string; }
DTO는 Data Transfer Object의 약자로 어떠한 Data type을 전달할 것인지를 결정한다. 크게 input과 output으로 나뉜다.
위 LoginInput을 보면 extends로 PIckeType을 가져온다. PickType은 mapped type으로 그 외에 PartialType, OmitType, IntersectionType 등이 있다. 첫번째 인자로는 Entity를, 두 번째 인자로는 해당 entity의 field array를 받는다. 위 PickType에서는 User entity를 받고 그 안에서 email과 password를 field로 가져온다. mapped types를 사용하고 싶다면 entity와 dto에 @InputType()을 사용해야 한다.
그리고 output dto는 @ObjectType()을 사용한다.
users.resolver.ts
import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { LoginInput, LoginOutput } from './dtos/login.dto'; import { User } from './entities/user.entity'; import { UsersService } from './users.service'; @Resolver((of) => User) export class UsersResolver { constructor(private readonly usersService: UsersService) {} @Mutation((returns) => LoginOutput) async login(@Args('input') loginInput: LoginInput): Promise<LoginOutput> { return this.usersService.login(loginInput); } @Query((returns) => String) hi(): string { return 'hello'; } }
REST API는 Controller라 하고 Graphql API는 Resolver라고 한다. 그리고 각 기능을 담당하는 로직은 service라고 표현한다. graphql에서 action은 query, mutation, subscription이 있다. query는 rest api의 get과 같고 mutation은 post와 같다. REST API는 post 이외에 put, delete 등이 있지만 graphql에서는 데이터를 수정하는 모든 행위를 mutation이라고 한다.
nest js에서는 mutation만 존재할 수 없고 반드시 1개 이상의 @Query()가 있어야 한다. 그래서 위에 간단한 fuction인 hi()가 있다.
constructor로 인자로 usersService를 주었다. 나는 인스턴스를 생성한 적도 없지만 Dependency Injection에 의해 usersService를 받아올 수 있게 되었다.
users.service.ts
import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { JwtService } from 'src/jwt/jwt.service'; import { Repository } from 'typeorm'; import { LoginInput, LoginOutput } from './dtos/login.dto'; import { User } from './entities/user.entity'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private readonly users: Repository<User>, private readonly jwtService: JwtService, ) {} async login({ email, password }: LoginInput): Promise<LoginOutput> { try { const user = await this.users.findOne({ email }); if (!user) { return { ok: false, error: 'User not found', }; } const passwordCorrect = await user.checkPassowrd(password); if (!passwordCorrect) { return { ok: false, error: 'Wrong password', }; } const token = this.jwtService.sign(user.id); return { ok: true, token }; } catch (e) { return { ok: false, error: "Couldn't create account" }; } } }
@Injectable()과 constructor에 @InjectRepository(User)를 통해서 User Repository를 inject 할 수 있다.
참고 자료
- 노마드 코더의 우버 이츠 클론 강의
- @Entity() : https://docs.nestjs.com/techniques/database#repository-pattern
- @ObjectType() : https://docs.nestjs.com/graphql/resolvers#object-types
- @InputType() : https://docs.nestjs.com/graphql/mapped-types#partial
- Module : https://docs.nestjs.com/modules
- Mapped types : https://docs.nestjs.com/graphql/mapped-types#pick
- Repository : https://docs.nestjs.com/techniques/database#repository-pattern
'Project using Nest.js > E-commerce App' 카테고리의 다른 글
nest.js authGuard and authUser decorator (0) 2021.09.02 JWT with nestJS (0) 2021.09.02 nestJS entity (0) 2021.09.02 Connect DB which is postgresql with nest.js (0) 2021.09.02 Graphql Configuration with nest.js (0) 2021.09.02 - @Entity() decorator는 entity를 정의할 수 있게 해준다. 그 안에 email, password 등의 필드들을 정의할 수 있다. TypeORM을 위한 코드다.