# มาเขียน NestJS กันเถอะ

#Dev#Javascript#Nestjs#Backend#Rest#Api#Framework

NestJS (opens new window) เป็น Node.js Framework สำหรับเขียน Web Backend เขียนโดยใช้ภาษา TypeScript (แต่ก็สามารถเขียนด้วย Javascript ได้นะ)

ความดีงามของ NestJS ก็คือ มันมาพร้อมกับโครงสร้างที่เหมาะสมกับการ Scale แถมยังพร้อมทำ DI (Dependency Injection) ได้อย่างง่ายดายด้วย และจะดีขึ้นไปอีกถ้าคุณเคยเขียน Angular มาก่อน เพราะโครงสร้างรูปแบบการทำงานจะเหมือนๆกันเลย ต่างกันที่ Angular เราเขียนเพื่อใช้ใน Frontend

Header

ตัว NestJS ถูกสร้างโดยมีพื้นฐานของ Express (opens new window) ทำงานอยู่เบื้องหลัง หรือจะเปลี่ยนให้ NestJS ไปใช้ Fastify (opens new window) แทนก็ยังได้

โดยตัว NestJS จะมี API ให้เราติดต่อกับ Express ได้ หรือถ้าเราจะจัดการ Express เองก็ทำได้เหมือนกัน (แต่อาจจะเจอปัญหาได้ ถ้าเกิดเราอยากย้ายจาก Express ไปใช้ Fastify)

# ติดตั้ง NodeJS

ถ้าเรายังไม่เคยติดตั้ง Node.js (opens new window) ก็ไปติดตั้งก่อนครับ

Image
จะใช้เวอร์ชันไหนก็เอาตามสะดวก

# ติดตั้ง NestJS

ต่อมาก็มาที่ Terminal / PowerShell / Command Line แล้วทำคำสั่ง

npm install -g @nestjs/cli

จะเป็นการติดตั้ง NestJS CLI เพื่อเรียกใช้คำสั่งของ NestJS

# สร้างโปรเจคใหม่

เราจะใช้คำสั่งนี้ในการสร้างโปรเจคใหม่ครับ (โปรเจคที่ได้จะเขียนโดยใช้ภาษา TypeScript)

nest new <project-name>

หากต้องการเขียนด้วย Javascript ธรรมดาให้ไปโหลดโครงโปรเจคได้ที่ Github (opens new window)

ทีนี้ก็มาลองสร้างโปรเจคกันจริงๆเลยครับ

nest new my-awesome-backend

ถ้ารันไม่ได้ให้ไปเพิ่ม Path ของ NPM ก่อนครับ เช่นใน
Windows ใช้ %APPDATA%\Roaming\npm

หรือเราสามารถใช้คำสั่ง npm config get prefix ในการหาตำแหน่ง Path นั้นก็ได้

เมื่อรันคำสั่งแล้วเราจะได้โปรเจคมาครับชื่อโฟลเดอร์ my-awesome-backend ให้เข้ามาที่โฟลเดอร์นี้แล้วสั่งรัน

npm run start หรือ npm run start:debug

Image

จะเป็นการเริ่มให้ NestJS ทำงานครับ โดยสามารถไปดูผลได้ที่ http://localhost:3000/ (opens new window)
ก็จะพบกับ Hello World!

# โครงสร้างโปรเจค

สิ่งที่เราสนใจ ณ ตอนนี้จะมีแค่ในโฟลเดอร์ที่มีชื่อว่า src ครับ โดยในโฟลเดอร์นี้จะเก็บโค้ดของเราทั้งหมดไว้ ซึ่งตามภาพก็จะเห็นสิ่งที่เราได้มาตอนสร้างโปรเจค

Image

main.ts - เป็นไฟล์เริ่มต้นของโปรเจค (ยังไม่ต้องทำอะไรกับมัน)

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

สิ่งที่เราสนใจหลักๆในตอนนี้ คือ

...
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
...

NestFactory.create จะทำการสร้าง NestJS Application โดยเบื้องต้นจะใช้ Express ในการทำงาน แต่ถ้าเราอยากใช้ Fastify เราสามารถกำหนดได้โดยการเพิ่มตามนี้ครับ

import { NestFastifyApplication } from '@nestjs/platform-fastify';
...
  const app = await NestFactory.create<NestFastifyApplication>(AppModule);
...

ต้องติดตั้ง npm install @nestjs/platform-fastify ก่อนนะครับ

ในส่วนของ app.listen เป็นฟังก์ชันให้เริ่มทำงานและรอ Request มาที่ Port 3000

# Module

ต่อมาจะเป็นไฟล์ Module เป็นไฟล์ที่ครอบคลุมส่วนประกอบหลักของเรา ซึ่งก็คือ Controller และ Service โดยเราจะสามารถสร้างหลายๆ Module หรือเอา Module ของคนอื่นมารวมร่างกับของเราก็ได้

Image

เช่น ถ้าเราอาจจะมี UserModule สำหรับเก็บส่วนที่เกี่ยวกับ User ทั้งหมด เป็นต้น

app.module.ts - ไฟล์ Module ของโปรเจคเรา เบื้องต้นมีแค่ไฟล์นี้ไฟล์เดียว

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

เราจะเห็นว่ามี @Module ใน TypeScript และหลายๆภาษาจะเรียกมันว่า Decorator ในที่นี้ใช้กำหนดว่า class AppModule ของเราจะใช้เป็น Module

โดยกำหนดด้วยว่า Module นี้จะประกอบไปด้วย Controller และ Service (Provider) อะไร

# Controller

ซึ่งใน Module ของเราตอนนี้มีใช้แค่ Controller เดียวครับ คือ app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

แน่นอนว่ามี @Module แล้วก็จะมี @Controller เพื่อใช้บอกว่าเราจะใช้ class นี้เป็น Controller ด้วยเหมือนกัน โดย Controller จะเป็นไฟล์ที่กำหนดว่าเมื่อผู้ใช้เรียก (Request) แล้วมันโปรแกรมของเราควรจะทำอะไรต่อ

Image

โดย Path ที่ผู้ใช้เรียกนั้นจะเป็น localhost:3000/ เพราะ @Controller ไม่ใส่พารามิเตอร์อะไร แต่ถ้าเราใส่ @Controller('api') ผู้ใช้จะต้องเรียกมาที่ localhost:3000/api แทนครับ

และในส่วนของ @Get เป็นการกำหนดว่า Function หรือ Method นั้นๆจะรับ Request มาเป็นแบบไหน ในที่นี้รับ Method GET โดยไม่มี Path เพิ่มเติม โดยเราสามารถกำหนดค่าให้มันได้ เช่น @Get('hello') เมื่ออยู่ใน @Controller('api') แล้ว

ผู้ใช้จะต้องทำการเรียกมาที่ localhost:3000/api/hello

และเรายังสามารถใช้ในรูปแบบ @Get(':text') ได้อีกด้วย เพื่อรอรับค่าที่ผู้ใช้ใส่มาตอนที่เรียก เช่น ผู้ใช้เรียก localhost:3000/api/intception ตัว Controller จะเก็บค่า intception ใส่ไว้ในตัวแปรชื่อว่า text ให้เราไปประมวลผลใช้ในภายหลัง

# Service หรือ Provider

ถัดมาจะเป็นส่วนของ Service app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

จะเห็นว่าในการกำหนด class ว่าเป็น Service เราจะใช้ Decorator ที่มีชื่อว่า @Injectable เป็นการบอกว่าสามารถเอาสิ่งที่ไปให้กับใครก็ได้ที่ต้องการใช้ Service นี้ ซึ่งในที่นี้เราเอาไปใช้ใน AppModule ครับ

...
  providers: [AppService],
...

และหากใครต้องการใช้งาน Service นี้ (ซึ่งในที่นี้ใช้ใน Controller) เราสามารถทำโดยการใส่ AppService ใน constructor ของ Controller ที่อยู่ใน Module นั้นๆ เช่น

...
  constructor(private readonly appService: AppService) {}
...

โดยเจ้า NestJS จะรู้ว่า Controller ตัวนี้ต้องการใช้ AppService มันก็จะจัดหามาให้เรา โดยที่เราไม่ต้องทำการสร้าง AppService เองเลยครับ ดังนั้นใน Controller ของเราจึงสามารถเรียกใช้

this.appService.getHello()

ได้แล้ว

และนี่ก็เป็นปฐมบทของ NestJS ครับ คราวหน้าจะมาต่อเรื่องอะไร ก็รอติดตามรับชมกันครับ

# สรุป NestJS

  1. วางโครงสร้างโปรเจคมาดีมากครับ เราแทบไม่ต้องมือเปรอะเปื้อนไปทำอะไรเลย
  2. มันทำ Dependency Injection ให้เราโดยไม่ต้องร้องขอ และอาจจะไม่รู้ตัวเลยด้วยซ้ำ
  3. หน้าตาโคตรเหมือน Angular ได้แรงบรรดาลใจมาแบบ 200% ใครชอบ Angular ก็ควรจะชอบอันนี้ล่ะครับ (แต่ถ้าคนเกลียดก็อาจจะเกลียดเหมือนกัน...)

อัปเดตเมื่อ: 1 ปีที่แล้ว