우버이츠클론코딩 #4
Entity
-> 데이터베이스에 저장되는 데이터의 형태를 보여주는 모델
-
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와 상호작용 담당
-
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포트가 안됐었음)
-> 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; }
-> 제대로 반영됨
※ 전에 환경설정(.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 } } }