มาลองใช้ NestJS ทำ GraphQL Server กัน (ฉบับผู้เริ่มต้น)
Tags: nestjs, graphql, apollo, typescript
Published: 2020-04-11 04:39:01



ทุกวันนี้ผมใช้ express + apollo ทำ GraphQL Server แต่ปีนี้ในทีมก็มีมติว่าจะเปลี่ยนไปเขียน TypeScript ซึ่งจะตอบโจทย์ในเรื่องของ NewBie Mistakes ตอนแรกว่าจะเขียนไว้อ่านเอง แต่ก็อยากมาแชร์ให้ทุก ๆ คนมาลองดูกันครับ

##มาทำความรู้จักกันครับว่า NestJS คืออะไร
NestJS อย่างที่หลาย ๆ คนทราบกันดีอยู่แล้วว่ามันคือ Framework ตัวนึงที่เขียนด้วย TypeScript และออกแบบมาโดยใช้แนวคิด OOP กับ Fuctional Programming เข้าด้วยกัน ซึ่ง NestJS ก็อาศัยการทำงานของ HTTP Server ที่เราสามารถเลือกใช้ได้ก็จะเป็น Express หรือ Fastify ในบทความนี้ผมขอใช้ Express แล้วกันนะครับ

##มาเริ่มกันเลยดีกว่า 
ติดตั้ง Nest CLI กันก่อน ด้วย Command ตามนี้
```shell
$ npm i -g @nestjs/cli
```
เสร็จแล้วก็สร้างโปรเจค
```shell
$ nest new nestjs-graphql-starter
```
แล้วมันจะขึ้นให้เลือก package manager ว่าจะใช้ `npm` หรือ `yarn` ในทีนี้ผมก็เลือก `yarn` นะครับ
```
Which package manager would you ❤️  to use? npm or yarn
```

###เท่านี้ก็จะได้ Nestjs project structer มาแล้วครับ ต่อมาเราจะมาเริ่มใช้ GrahpQL ร่วมกับ NestJS กัน
ก่อนอื่นก็มาติดตั้ง GraphQL กันด้วยคำสั่ง
```shell
$ yarn add @nestjs/graphql graphql-tools graphql // or npm i --save @nestjs/graphql graphql-tools graphql
```
ต่อมาติดติ้ง Http server จะเลือกใช้ตัวไหนก็ได้ระหว่าง express กับ fastify

กรณีเลือกใช้ express ใช้คำสั่ง
```shell
$ yarn add apollo-server-express // or npm i --save apollo-server-express
```

กรณีเลือกใช้ fastify ใช้คำสั่ง
```shell
$ yarn add apollo-server-fastify // or npm i --save apollo-server-fastify
```

เข้า Folder ที่สร้างเมื่อกี้ด้วย Editor Tool แล้วหาไฟล์ `nest-cli.json` ใส่ code ตามนี้

```json
{
  "compilerOptions": {
    "plugins": ["@nestjs/graphql/plugin"]
  }
}
```

ต่อมาแก้ไขไฟล์ `./src/app.module.ts` เพื่อเรียกใช้งาน graphql ตาม code ด้านล่างนี้ได้เลยครับ
```typescript
import { Module } from '@nestjs/common'
import { GraphQLModule } from '@nestjs/graphql'

@Module({
  imports: [
    GraphQLModule.forRoot({
      installSubscriptionHandlers: true,
      autoSchemaFile: 'schema.gql',
      debug: false,
      playground: true,
    }),
  ],
})
export class AppModule {}
```

####จาก code ด้านบน 
`installSubscriptionHandlers: true` ไว้สำหรับเลือกเปิดใช้งาน Subscription หรือไม่
`autoSchemaFile: 'schema.gql'` ไว้ generate file typeDefs schema เพื่อนำไปไว้ Build เพื่อ Run Apollo Server 
`debug: false` ไว้สำหรับจะเลือกใช้งาน debug หรือไม่ 
`playground: true` ไว้สำหรับเลือกเปิดใช้งาน playgroud client หรือไม่

ต่อมาพระเอกของเรากันดีกว่า Nest มี CLI ไว้สร้าง Module,Interface,Resolver,Service
มาสร้าง module กัน ด้วยคำสั่ง
```shell
$ nest g module {name} //or nest g mo {name}
```
ตัวอย่างผมจะสร้าง module ชื่อ book ก็ใช้คำสั่ง
```shell
$ nest g mo book
```

```shell
$ nest g resolver {name} //or nest g r {name}
```
ตัวอย่างผมจะสร้าง Resolver ชื่อ book ก็ใช้คำสั่ง
```shell
$ nest g r book
```

ทีนี้ NestJS ก็ได้ออก Decorator ของตัวเองเพื่อทำ DTO (Data Transfer Object) ไว้แล้ว โดยที่เมื่อก่อนยืมของ `TypeGraphQL` มาใช้ก่อน งั้นเรามาเริ่มสร้าง Model กัน
สร้างโดยการ Create Folder ให้อยู่ใน Directory Book ที่ `./src/book/models/book.model.ts`

```typescript
import { Field, ID, ObjectType } from '@nestjs/graphql'

@ObjectType()
export class Book {
  @Field(type => ID)
  id: string

  @Field(type => String, { nullable: true })
  name?: string

  @Field(type => String, { nullable: true })
  description?: string

  @Field(type => Number, { nullable: true })
  price?: number

  @Field(type => [String], { nullable: true })
  writer?: string[]
}
```

ถ้าต้องการให้ Field มีค่าเป็น null ได้ ให้ใส่ options เพิ่มคือ `{ nullable: true }` และใส่ ? หลังตัวแปรเพื่อละเว้นให้เป็น optional ได้

####เสร็จแล้วสร้าง DTO ไว้หรับ Type Input กันเถอะเพื่อไปใช้เป็น Variable ตอนเรียกใช้งานกัน

create folder dto ให้อยู่ใน Directory Book ที่ `./src/book/dto`
แล้ว create file `book.input.ts` อยู่ใน folder dto
เมื่อสร้างแล้วเขียน code ตามนี้เลยครับ

```typescript
import { Field, InputType } from '@nestjs/graphql'

@InputType()
export class InputBook {
  @Field(type => String)
  name: string

  @Field(type => Number, { nullable: true })
  price?: number

  @Field(type => String, { nullable: true })
  description?: string

  @Field(type => [String])
  writer: string[]
}
```

ถ้าต้องการให้ Field ไหนเป็น Array ให้ใส่ [] คอบ Type นั้นได้เลยครับ


###มาจัดการกับ Resolver กัน ในบทความที่ผมเขียนนี้จะยกการทำ Mutation กับ Query ไว้เท่านี้ก่อนนะครับเพื่อไม่ให้บทความมีความยาวมากเกินไป

กลับมาที่ File : `book.resolver.ts` แล้วเขียน Code สร้าง Mutation กับ Query กันตามนี้เลยครับ ขออนุญตาติฟิกค่า return เลยนะครับ

```typescript
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql'
import { Book } from './models/book.model'
import { InputBook } from './dto/book.input'

@Resolver('Book')
export class BookResolver {
  @Query(returns => Book)
  getBook(): Book {
    const result: Book = {
      id: '1',
      name: 'BeforeSecond',
      price: 199,
    }
    return result
  }

  @Mutation(returns => Book)
  createBook(@Args('input') input: InputBook): Book {
    const result: Book = {
      id: '2',
      ...input,
    }
    return result
  }
}
```

เสร็จแล้วลองรันกันครับ
```shell
$ yarn start:dev
```

เข้า Web Browser เปิด URL: `http://localhost:3000/graphql`
####ทดสอบ Query กัน
ใส่ Code Query ดังนี้
```
query {
  getBook{
    id
    name
    price
    description
    writer
  }
}
```

ปรากฏว่าได้ผลลัพทธ์
```json
{
  "data": {
    "getFirst": {
      "id": "1",
      "name": "first"
    }
  }
}
```

####ทดสอบ Mutation กัน

```
mutation {
  createBook(input:{
    name:"test",
    description:"ทดสอบ",
    price:299,
    writer:["First","BeforeSecond"]
  }){
    id
    name
    price
    description
    writer
  }
}
```

ผลลัพท์
```json
{
  "data": {
    "createBook": {
      "id": "2",
      "name": "test",
      "price": 299,
      "description": "ทดสอบ",
      "writer": [
        "First",
        "BeforeSecond"
      ]
    }
  }
}
```
Very Good 👍👍👍👍👍

####สรุปได้ว่า NestJS ก็เป็นอีกทางเลือกนึงที่สามารถทำ GraphQL Server ได้ด้วยการเขียน TypeScript ที่ผมคิดว่าตอบโจทย์ได้ดีเลยทีเดียวครับ โดยปกติถ้าเขียน Express+Apollo เราจะต้องมานั่งทำ typeDefs เอง หรือบ้างก็ไปใช้ CLI codegen:generate แต่ NestJS ทำให้เราสะดวกมากขึ้นไปเยอะเลย ยิ่งถ้าใช้ TypeORM หรือ Sequelize นี่ยิ่งสบายเลยกับการเขียน GraphQL มาก ๆ เลยครับ
สุดท้ายนี้ ถ้าผิดพลาดต้องขอภัยด้วยนะครับ ขอบคุณที่อ่านมาจนจบครับผม 😀
สามารถศึกษาเพิ่มเติมได้ที่
Referense: https://docs.nestjs.com/graphql/quick-start