Entity

-> 데이터베이스에 저장되는 데이터의 형태를 보여주는 모델

  1. entity 생성(TypeORM에서 사용가능한)

    • restaurant.entity.ts

      import { Field, ObjectType } from "@nestjs/graphql";
      import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
           
      //클래스 하나로 graphAL schema와 DB에 저장되는 실제 데이터 형식을 만들 수 있음
      //DB에 model 생성, 자동으로 graphQL에 스키마 작성
      @ObjectType() //GraphQL을 위한 ObjectType
      @Entity()//TypeORM을 위한 Entity
      export class Restaurant{//Restaurant을 위한 object type 생성
           
          @PrimaryGeneratedColumn()
          @Field(type => Number)
          id: number;
           
          @Field(is => String)//GraphQL
          @Column()//TypeORM
          name: string;
           
          @Field(type => Boolean)
          @Column()
          isVegan: boolean;
           
          @Field(type => String)
          @Column()
          address:string;
           
          @Field(type => String)
          @Column()
          ownersName:string;
           
          @Field(type => String)
          @Column()
          categoryName: string;
           
      }
      
    • app.module.ts

      -> entities 추가

      @Module({
        imports: [
          ConfigModule.forRoot({
            isGlobal: true,
            envFilePath: process.env.NODE_ENV == "dev" ? ".dev.env" : ".test.env",
            ignoreEnvFile: process.env.NODE_ENV ==="prod",
          }),
          TypeOrmModule.forRoot({
            type: 'postgres',
            host: 'localhost',
            port: 5432,
            username: 'postgres',
            password: '1234',//localhost인 경우엔 안써도 됨
            database: 'nuber-eats',
            synchronize: true,
            logging: true,
            entities:[Restaurant]//DB에 TypeORM entity 넣도록 요청
          }),
      

TypeScript로 DB 접근

Data mapper

-> DB와 상호작용하는 패턴

-> Repository를 test하고 simulate할 수 있음

  • repository

    Entity와 상호작용 담당

  1. repository import하기

    • restaurants.module
    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { Restaurant } from './entities/restaurant.entity';
    import { RestaurantResolver } from './restaurants.resolvers';
       
    @Module({
        imports: [TypeOrmModule.forFeature([Restaurant])], //entity import
        providers:[RestaurantResolver],
    })
    export class RestaurantsModule {}
    
    • entities에 restaurants.service.ts 생성

      import { Injectable } from "@nestjs/common";
           
          @Injectable()
          export class RestaurantService{}
      
    • restaurants.resolvers.ts

      ->constructor 추가

      import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
      import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
           
      import { Restaurant } from "./entities/restaurant.entity";
      import { RestaurantService } from "./entities/restaurants.service";
           
           
      @Resolver(of => Restaurant)
      export class RestaurantResolver {
          constructor(private readonly restaurantService: RestaurantService){}
          @Query(returns => [Restaurant]) //리턴 restaurant array
          restaurants(@Args('veganOnly') veganOnly: boolean): Restaurant[] { //veganOnly라는 argument 호출
              console.log(veganOnly)
              return [];   
          }
           
          @Mutation(returns => Boolean)
          createRestaurant( //arguments 만들기
              @Args() CreateRestaurantDto: CreateRestaurantDto
          ): boolean{
              console.log(CreateRestaurantDto)
              return true;
          }
      }
      

      -> RestaurantService를 RestaurantResolver에 inject하려고

    • restaurants.module.ts

      -> provider에 RestaurantService 추가

      import { Module } from '@nestjs/common';
      import { TypeOrmModule } from '@nestjs/typeorm';
      import { Restaurant } from './entities/restaurant.entity';
      import { RestaurantService } from './entities/restaurants.service';
      import { RestaurantResolver } from './restaurants.resolvers';
           
      @Module({
          imports: [TypeOrmModule.forFeature([Restaurant])], //entity import
          providers:[RestaurantResolver, RestaurantService],
      })
      export class RestaurantsModule {}
      
    • restaurants.service.ts

      import { Injectable } from "@nestjs/common";
      import { InjectRepository } from "@nestjs/typeorm";
      import { Repository } from "typeorm";
      import { Restaurant } from "./restaurant.entity";
           
          @Injectable()
          export class RestaurantService{
                  constructor(@InjectRepository(Restaurant)private readonly restaurants: Repository<Restaurant>,
                  ){}//repository(restaurants)를 inject하기
              getAll(): Promise<Restaurant[]>{//모든 restaurant를 가져오는 service
                  return this.restaurants.find(); 
              }
          }
      

      -> port 3002에서 확인(원래 사용하던 3000포트가 안됐었음)

      image-20210714154107776

      -> DB에 접근하여 빈 restaurants 배열 가져옴(DB 접근 성공)

Restaurant 생성

1.method 만들기

  • method

    class안에 있는 function

  • restaurants.service.ts

    -> createRestaurant 생성

    import { Injectable } from "@nestjs/common";
    import { InjectRepository } from "@nestjs/typeorm";
    import { Repository } from "typeorm";
    import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
    import { Restaurant } from "./entities/restaurant.entity";
      
        @Injectable()
        export class RestaurantService{
            constructor(
                @InjectRepository(Restaurant)
                private readonly restaurants: Repository<Restaurant>,
            ){}//repository(restaurants)를 inject하기
      
            getAll(): Promise<Restaurant[]>{//모든 restaurant를 가져오는 service
                return this.restaurants.find();  //restaurants는 restaurant entity의 repository
            }
      
            createRestaurant(createRestaurantDto: CreateRestaurantDto
                ): Promise<Restaurant>{//return 타입이 restaurant
                const newRestaurant = this.restaurants.create(createRestaurantDto);//새로운 instance를 만든 것으로, DB는 전혀 건들지 않음
                return this.restaurants.save(newRestaurant)//save method로 DB에 저장
            }
        }
    
  • restaurant.resolver.ts

    -> restaurant가 잘 생성되는지 확인을 위해 Mutation에서 try, catch 문 사용

    import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
    import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
      
    import { Restaurant } from "./entities/restaurant.entity";
    import { RestaurantService } from "./restaurants.service";
      
      
    @Resolver(of => Restaurant)
    export class RestaurantResolver {
        constructor(private readonly restaurantService: RestaurantService){}
        @Query(returns => [Restaurant]) //리턴 restaurant array
        restaurants(): Promise<Restaurant[]> { //veganOnly라는 argument 호출
            return this.restaurantService.getAll();   
        }
      
        @Mutation(returns => Boolean)
        async createRestaurant( //arguments 만들기
            @Args() CreateRestaurantDto: CreateRestaurantDto
        ): Promise<boolean>{ //async 사용시 promise와 value 써야함
            try { //restaurant가 잘 생성되는지 try, catch로 판단
                await this.restaurantService.createRestaurant(CreateRestaurantDto);
                return true;
            }
            catch(e){
                console.log(e)
                return false;
            }
        }
    }
    

2. entity 업데이트시 dto에도 업데이트 해야함

  • Mapped types

    -> base type을 바탕으로 다른 버전을 만들수 있게 함

  • restaurants.resolvers.ts

    -> Args를 input으로!

        @Mutation(returns => Boolean)
        async createRestaurant( //arguments 만들기
            @Args('input') CreateRestaurantDto: CreateRestaurantDto
        ): Promise<boolean>{ //async 사용시 promise와 value 써야함
            try { //restaurant가 잘 생성되는지 try, catch로 판단
                await this.restaurantService.createRestaurant(CreateRestaurantDto);
                return true;
            }
            catch(e){
                console.log(e)
                return false;
            }
        }
    
  • create-restaurant.dto.ts

    -> InputType으로 변경

    import { ArgsType, Field, InputType, OmitType } from "@nestjs/graphql";
    import { IsBoolean, IsString, Length } from "class-validator";
    import { Restaurant } from "../entities/restaurant.entity";
      
    @InputType() //(ArgsType)각각을 분리된 arguments로써 정의 & 분리된 값을 GraphQL argument로 전달
    export class CreateRestaurantDto extends OmitType(Restaurant, ["id"], InputType) {}//id 제외하고 다 가져오기 &  OmitType은 decorator 바꾸도록 허락
    //ObjectType으로 나오는 것을 InputType으로 바꿈
    

    또는 restaurants.entity에 가서 InputType 생성하는 방법도 있음

  • restaurant.entity.ts

    import { Field, InputType, ObjectType } from "@nestjs/graphql";
    import { IsBoolean, IsOptional, IsString, Length } from "class-validator";
    import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
      
    //클래스 하나로 graphAL schema와 DB에 저장되는 실제 데이터 형식을 만들 수 있음
    //DB에 model 생성, 자동으로 graphQL에 스키마 작성
      
    @InputType({ isAbstract: true })//InputType이 스키마에 포함되지 않도록
    @ObjectType() //GraphQL을 위한 ObjectType
    @Entity()//TypeORM을 위한 Entity
    export class Restaurant{//Restaurant을 위한 object type 생성
        @PrimaryGeneratedColumn()
        @Field(type => Number)
        id: number;
          
        @Field(type => String)//GraphQL
        @Column()//TypeORM
        @IsString()
        @Length(5)
        name: string;
      
        @Field(type => Boolean)
        @Column()
        @IsBoolean()
        isVegan: boolean;
      
        @Field(type => String)
        @Column()
        @IsString()
        address: string;
      
        @Field(type => String)
        @Column()
        @IsString()
        ownersName: string;
      
        @Field(type => String)
        @Column()
        @IsString()
        categoryName: string;
      
    }
    

    image-20210714170345562

    -> 제대로 반영됨

※ 전에 환경설정(.env.test, .env.dev)를 하면 오류가 나서 스킵했더니 createRestaurant에서 False가 나옴..

3. mutation 생성(restaurant update하는 mutation)

  • restaurants.resolvers.ts

    -> Mutation 추가

    import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
    import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
    import { updateRestaurantDto } from "./dtos/update-restaurant.dto";
    import { Restaurant } from "./entities/restaurant.entity";
    import { RestaurantService } from "./restaurants.service";
      
      
    @Resolver(of => Restaurant)
    export class RestaurantResolver {
        constructor(private readonly restaurantService: RestaurantService){}
        @Query(returns => [Restaurant]) //리턴 restaurant array
        restaurants(): Promise<Restaurant[]> { //veganOnly라는 argument 호출
            return this.restaurantService.getAll();   
        }
        @Mutation(returns => Boolean)
        async createRestaurant( //arguments 만들기
            @Args('input') CreateRestaurantDto: CreateRestaurantDto
        ): Promise<boolean> { //async 사용시 promise와 value 써야함
            try { //restaurant가 잘 생성되는지 try, catch로 판단
                await this.restaurantService.createRestaurant(CreateRestaurantDto);
                return true;
            }
            catch(e){
                console.log(e)
                return false;
            }
        }
      
        @Mutation(returns => Boolean)
        async updateRestaurant(
            @Args('id') id: number,
            @Args('data') data: updateRestaurantDto,
        ) {
            return true
        }
      
          
    }
      
    
  • dto 폴더에 update-restaurant.dto.ts 파일 생성

    • update-restaurant.dto.ts

      import { InputType, PartialType } from "@nestjs/graphql";
      import { CreateRestaurantDto } from "./create-restaurant.dto";
          
      @InputType()
      export class updateRestaurantDto extends PartialType(CreateRestaurantDto) {}
      //PartialType은  type의 모든 property를 말하지만 옵션사항임
      
  • id와 UpdateRestaurantDto 합치기

    • create-restaurant.dto.ts

      import { ArgsType, Field, InputType, Int, PartialType } from "@nestjs/graphql";
      import { CreateRestaurantDto } from "./create-restaurant.dto";
          
      @InputType()
      export class updateRestaurantDtoInputType extends PartialType(CreateRestaurantDto) {}
      //PartialType은  type의 모든 property를 말하지만 옵션사항임
      //partialType에서 createRestaurantDto 사용하는 이유가 id 때문
          
      @ArgsType()
      export class updateRestaurantDto {
          @Field(type => Number)
          id: number;
          
          @Field(type => updateRestaurantDtoInputType)
          data: updateRestaurantDtoInputType;
      }
      
    • restaurants.resolvers.ts

      -> InputType 필요 없음

          
      import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
      import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
      import { updateRestaurantDto } from "./dtos/update-restaurant.dto";
      import { Restaurant } from "./entities/restaurant.entity";
      import { RestaurantService } from "./restaurants.service";
          
          
      @Resolver(of => Restaurant)
      export class RestaurantResolver {
          constructor(private readonly restaurantService: RestaurantService){}
          @Query(returns => [Restaurant]) //리턴 restaurant array
          restaurants(): Promise<Restaurant[]> { //veganOnly라는 argument 호출
              return this.restaurantService.getAll();   
          }
          @Mutation(returns => Boolean)
          async createRestaurant( //arguments 만들기
              @Args('input') CreateRestaurantDto: CreateRestaurantDto
          ): Promise<boolean> { //async 사용시 promise와 value 써야함
              try { //restaurant가 잘 생성되는지 try, catch로 판단
                  await this.restaurantService.createRestaurant(CreateRestaurantDto);
                  return true;
              }
              catch(e){
                  console.log(e)
                  return false;
              }
          }
          
          @Mutation(returns => Boolean) //UpdateRestaurantDto 합쳐줘서 InputType 필요 없음 & ArgsType만 필요
          async updateRestaurant(
              @Args() updateRestaurantDto: updateRestaurantDto) {
              return true
          }
             
      }
      
      • InputType이면 Args 이름 지정해야함

4. service에 update 만들기

  • restaurants.service.ts

    import { Injectable } from "@nestjs/common";
    import { InjectRepository } from "@nestjs/typeorm";
    import { Repository } from "typeorm";
    import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
    import { updateRestaurantDto } from "./dtos/update-restaurant.dto";
    import { Restaurant } from "./entities/restaurant.entity";
      
        @Injectable()
        export class RestaurantService{
            constructor(
                @InjectRepository(Restaurant)
                private readonly restaurants: Repository<Restaurant>,
            ){}//repository(restaurants)를 inject하기
      
            getAll(): Promise<Restaurant[]>{//모든 restaurant를 가져오는 service
                return this.restaurants.find();  //restaurants는 restaurant entity의 repository
            }
      
            createRestaurant(
                createRestaurantDto: CreateRestaurantDto
            ): Promise<Restaurant>{//return 타입이 restaurant
                const newRestaurant = this.restaurants.create(createRestaurantDto);//새로운 instance를 만든 것으로, DB는 전혀 건들지 않음
                return this.restaurants.save(newRestaurant);//save method로 DB에 저장
            }
            updateRestaurant({id, data}:updateRestaurantDto){
                return this.restaurants.update(id, {...data}) //update하고 싶은 entity의 field 보내야함 & 저기엔 update 하고 싶은 object의 id 넣기
            } //저 id를 가진 Restaurant를 update
            //update()sms db에 해당 entity가 있는지 확인 안함
        }
    
  • restaurants.resolvers.ts

      
    import { Args, Mutation, Query, Resolver } from "@nestjs/graphql";
    import { CreateRestaurantDto } from "./dtos/create-restaurant.dto";
    import { updateRestaurantDto } from "./dtos/update-restaurant.dto";
    import { Restaurant } from "./entities/restaurant.entity";
    import { RestaurantService } from "./restaurants.service";
      
      
    @Resolver(of => Restaurant)
    export class RestaurantResolver {
        constructor(private readonly restaurantService: RestaurantService){}
        @Query(returns => [Restaurant]) //리턴 restaurant array
        restaurants(): Promise<Restaurant[]> { //veganOnly라는 argument 호출
            return this.restaurantService.getAll();   
        }
        @Mutation(returns => Boolean)
        async createRestaurant( //arguments 만들기
            @Args('input') CreateRestaurantDto: CreateRestaurantDto
        ): Promise<boolean> { //async 사용시 promise와 value 써야함
            try { //restaurant가 잘 생성되는지 try, catch로 판단
                await this.restaurantService.createRestaurant(CreateRestaurantDto);
                return true;
            }
            catch(e){
                console.log(e)
                return false;
            }
        }
      
        @Mutation(returns => Boolean) //UpdateRestaurantDto 합쳐줘서 InputType 필요 없음 & ArgsType만 필요
        async updateRestaurant(
            @Args() updateRestaurantDto: updateRestaurantDto
            ) {
            try {
                await this.restaurantService.updateRestaurant(updateRestaurantDto);
            }catch(e){
                console.log(e);
                return false
            }
        }
          
    }