ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • login API on nestJS using graphql
    Project 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;
    }

     

    • @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 할 수 있다.

    참고 자료

    '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

    댓글

Designed by Tistory.