Section 1

นักศึกษากำลังตั้งค่าเครื่องมือพัฒนา สร้าง NestJS API แรก และเชื่อมเส้นทาง API กับเบราว์เซอร์
SECTION 1 · เริ่มสร้าง

ตั้งค่าเครื่องมือ + สร้าง API ตัวแรก


⚠️ อ่านก่อนเริ่ม

  1. จำหมายเลขเครื่องของตัวเอง แล้วนั่งเครื่องเดิมทุก Section โปรเจกต์และฐานข้อมูลจะอยู่บนเครื่องนี้ตลอดเทอม (เครื่องไม่รีเซ็ตเมื่อรีบูต งานจะยังอยู่)

Part 1 — เตรียมเครื่อง

🗺️ ตำแหน่งตอนนี้ในระบบ

  • 🟢 เตรียมเครื่องมือ ← กำลังทำส่วนนี้
  • ⚪ Angular (frontend) (ยังไม่ถึง)
  • ⚪ NestJS (backend) (ยังไม่ถึง)
  • ⚪ PostgreSQL (ฐานข้อมูล) (ยังไม่ถึง)

ต่อไป: เปิดโปรเจกต์ตั้งต้นแล้วรัน backend ให้ทำงานครั้งแรก

💡 ศัพท์ที่จะเจอตลอดทั้งวิชา — frontend คือส่วนที่ผู้ใช้เห็นและกดใช้งาน (ในวิชานี้คือ Angular) backend คือส่วนที่ทำงานอยู่เบื้องหลัง จัดการข้อมูล (ในวิชานี้คือ NestJS) — จะอธิบายละเอียดพร้อมภาพประกอบอีกครั้งในขั้น 2.6

1.1 ติดตั้งเครื่องมือ 2 ตัว

ตัวที่หนึ่ง — Node.js 24 LTS

ทำไมต้องมี: เป็นโปรแกรมที่ทำให้รันภาษา JavaScript บนเครื่องเราได้ ปกติ JavaScript รันได้แต่ในเบราว์เซอร์ Node.js ทำให้ JavaScript รันเป็นโปรแกรมเดี่ยว ๆ บนเครื่องได้ ทั้ง NestJS และ Angular ต้องอาศัยตัวนี้ทำงาน

  1. เปิดเบราว์เซอร์ไปที่ nodejs.org
  2. หน้าแรกจะมีปุ่มดาวน์โหลดสองปุ่ม ให้กดปุ่มที่เขียนว่า LTS (มีคำว่า Recommended for Most Users กำกับอยู่) ห้ามกดปุ่ม Current แม้จะแสดงเลขเวอร์ชันสูงกว่าก็ตาม เพราะ Current คือรุ่นทดลองที่ยังไม่เสถียร
  3. ไฟล์ที่ได้จะชื่อประมาณ node-v24.x.x-x64.msi (ลงท้ายด้วย x64 แปลว่าเป็นรุ่น 64 บิต ซึ่งตรงกับเครื่องแลปเกือบทุกเครื่อง)
  4. ดับเบิลคลิกไฟล์ที่ดาวน์โหลดมาเพื่อเริ่มติดตั้ง

ระหว่างติดตั้ง จะเจอหน้าจอเรียงกันแบบนี้ ให้ทำตาม:

หน้าจอ ต้องทำอะไร
Welcome กด Next
End-User License Agreement ติ๊ก "I accept the terms..." แล้วกด Next
Destination Folder ปล่อยเป็นค่า default (C:\Program Files\nodejs\) กด Next
Custom Setup ปล่อยทุกอย่างเป็นค่า default กด Next — อย่าไปปิดตัวเลือกไหน
Tools for Native Modules ถ้ามีกล่องติ๊กเขียนว่า Automatically install the necessary tools ไม่ต้องติ๊ก (ไม่จำเป็นสำหรับวิชานี้ และจะทำให้ติดตั้งช้าลงมาก เพราะต้องดาวน์โหลด Visual Studio Build Tools เพิ่ม)
Ready to Install กด Install
จบการติดตั้ง กด Finish

⚠️ ต้องเป็น Node.js เวอร์ชัน 24 เท่านั้น เวอร์ชัน 22 จะทำให้ Angular ใน Section 3 ใช้งานไม่ได้เลย ไม่ใช่แค่แจ้งเตือน แต่จะปฏิเสธการทำงานทันที

💡 npm ติดมากับ Node.js อัตโนมัติในตัวติดตั้งเดียวกัน ไม่ต้องไปหาโหลดแยก — เป็นเหมือนร้านขายเครื่องมือสำเร็จรูป เวลาเราต้องการของที่คนอื่นเขียนไว้แล้ว ก็สั่ง npm ไปหยิบมาให้

ตัวที่สอง — VS Code

ทำไมต้องมี: เป็นโปรแกรมที่เราใช้พิมพ์โค้ดตลอดทั้งเทอม

  1. เปิดเบราว์เซอร์ไปที่ code.visualstudio.com
  2. กดปุ่มดาวน์โหลดสีน้ำเงินตรงกลางหน้า (จะดาวน์โหลดตัวติดตั้งของ Windows ให้อัตโนมัติ)
  3. ดับเบิลคลิกไฟล์ที่ได้ (ชื่อประมาณ VSCodeUserSetup-x64-x.xx.x.exe)

ระหว่างติดตั้ง จุดที่ต้องระวังเป็นพิเศษ:

หน้าจอ ต้องทำอะไร
License Agreement เลือก "I accept the agreement" กด Next
Select Destination Location ปล่อยค่า default กด Next
Select Additional Tasks นี่คือหน้าจอสำคัญที่สุด — ต้องติ๊กช่อง "Add to PATH" ให้ครบ (บางเวอร์ชันเขียนว่า "Add "Open with Code" action" ติ๊กได้ตามสบาย แต่ "Add to PATH" ต้องติ๊กเสมอ)
Ready to Install กด Install
จบการติดตั้ง ติ๊กหรือไม่ติ๊ก "Launch Visual Studio Code" ก็ได้ แล้วกด Finish

⚠️ ทำไม "Add to PATH" ถึงสำคัญมาก ถ้าไม่ติ๊กช่องนี้ พิมพ์คำสั่ง code . ใน PowerShell ภายหลังจะไม่ทำงาน (ขึ้นว่า is not recognized เหมือนตอนลืมติดตั้ง Node) ต้องไปถอนแล้วติดตั้งใหม่ ถ้าพลาดไปแล้วให้ถอนการติดตั้ง (Settings → Apps) แล้วโหลดตัวติดตั้งมาลงใหม่ คราวนี้อย่าลืมติ๊ก

1.2 ตรวจว่าติดตั้งสำเร็จ

เปิด PowerShell (กดปุ่ม Windows แล้วพิมพ์ powershell)

💡 Terminal / PowerShell คือหน้าจอที่เราพิมพ์คำสั่งสั่งงานคอมพิวเตอร์ตรง ๆ แทนการคลิกเมาส์ นักพัฒนาใช้ตลอดเวลาเพราะเร็วกว่า

powershell
node -v
npm -v
code --version

✅ ต้องเห็น

text
v24.x.x     ← ถ้าเป็น v22 ให้ถอนแล้วติดตั้งใหม่
10.x.x
1.xx.x      ← เลขเวอร์ชันของ VS Code ตามด้วยอีก 2 บรรทัดเป็นรหัส commit และสถาปัตยกรรมเครื่อง

❌ ถ้าขึ้น is not recognized ตัวใดตัวหนึ่ง → ปิดหน้าต่าง PowerShell ทั้งหมดแล้วเปิดใหม่ก่อน (เครื่องเพิ่งรู้จักโปรแกรมใหม่ หน้าต่างเก่าที่เปิดค้างไว้จะยังไม่เห็น)

❌ ถ้าเปิดหน้าต่างใหม่แล้วยังไม่หาย → แปลว่าตอนติดตั้งไม่ได้ติ๊ก "Add to PATH" (สำหรับ VS Code) ให้ถอนแล้วลงใหม่ตามขั้นตอนข้างบน หรือสำหรับ Node.js ให้ลองรันตัวติดตั้งซ้ำแล้วเลือก "Repair"


Part 2 — เปิดโปรเจกต์

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • ⚪ Angular (frontend) (ยังไม่ถึง)
  • 🟢 NestJS (backend) ← กำลังทำส่วนนี้
  • ⚪ PostgreSQL (ฐานข้อมูล) (ยังไม่ถึง)

ต่อไป: ฝึกภาษา TypeScript ที่จะใช้เขียน backend

2.1 สร้างโครงโปรเจกต์

powershell
cd C:\
mkdir webdev
cd webdev
mkdir booklab
cd booklab
npm init -y

💡 npm init -y = สร้างไฟล์ package.json เปล่า ๆ ให้อัตโนมัติ (ตัว -y คือตอบ "ใช่" ทุกคำถามแทนเรา) ไฟล์นี้คือบัตรประจำตัวของโปรเจกต์ บอกว่าโปรเจกต์ชื่ออะไร ใช้เครื่องมืออะไรบ้าง

✅ ต้องเห็นไฟล์ package.json เปิดดูจะมีเนื้อหาประมาณนี้

json
{
  "name": "booklab",
  "version": "1.0.0",
  ...
}

2.2 ตั้งค่าให้เป็น monorepo

โปรเจกต์เราจะมีทั้ง backend (NestJS) และ frontend (Angular) อยู่ในที่เดียวกัน แบบนี้เรียกว่า monorepo (repo เดียว หลายโปรเจกต์ย่อยข้างใน)

แผนภาพ Monorepo: repository เดียวมีโฟลเดอร์ apps ที่แยก NestJS API และ Angular web เป็นโปรเจกต์ย่อย

เปิดไฟล์ package.json ด้วย VS Code (code . เปิดทั้งโฟลเดอร์ก็ได้) แล้วแก้ให้เป็นแบบนี้

json
{
  "name": "booklab",
  "version": "1.0.0",
  "private": true,
  "workspaces": ["apps/api", "apps/web"],
  "scripts": {
    "dev:api": "npm run start:dev --workspace api",
    "dev:web": "npm start --workspace web",
    "dev": "concurrently -n API,WEB -c blue,green \"npm run dev:api\" \"npm run dev:web\""
  }
}

💡 workspaces = บอก npm ว่าในโฟลเดอร์นี้มีโปรเจกต์ย่อยอยู่ที่ไหนบ้าง ทำให้สั่งติดตั้งของทุกโปรเจกต์ย่อยได้ด้วยคำสั่งเดียวจาก root และแชร์ node_modules ก้อนเดียวกันได้ ประหยัดพื้นที่และเวลา

💡 private: true = ป้องกันการ publish โปรเจกต์นี้ขึ้น npm โดยไม่ตั้งใจ (ไม่มีผลอะไรกับเรา แต่เป็นธรรมเนียมที่ควรใส่)

⚠️ ตอนนี้โฟลเดอร์ apps/api และ apps/web ยังไม่มีอยู่จริง — ไม่เป็นไร npm จะข้ามไปเงียบ ๆ ถ้ายังไม่เจอ พอเราสร้างโฟลเดอร์จริงในขั้นต่อไปแล้วค่อยเชื่อมกันเอง

2.3 ติดตั้งเครื่องมือช่วยรันสองฝั่งพร้อมกัน

powershell
npm install --save-dev concurrently

💡 concurrently = เครื่องมือที่รันหลายคำสั่งพร้อมกันใน Terminal เดียว เราจะใช้ตอนอยากรันทั้ง backend และ frontend พร้อมกันด้วยคำสั่ง npm run dev (สร้างไว้ให้แล้วในขั้นที่แล้ว) แต่วันนี้ยังไม่ได้ใช้ เพราะมีแค่ backend

✅ ต้องเห็น โฟลเดอร์ node_modules และไฟล์ package-lock.json เกิดขึ้น

2.4 สร้างโปรเจกต์ NestJS (backend)

powershell
mkdir apps
cd apps
npm exec -- @nestjs/cli@latest new api --package-manager npm --skip-git --skip-install

ระหว่างรอ (ใช้เวลาสักครู่) อ่านความหมายของคำสั่งนี้

💡 npm exec -- = สั่งรันเครื่องมือโดยไม่ต้องติดตั้งค้างไว้ในเครื่องตลอดไป ถ้ามีติดตั้งอยู่ในโปรเจกต์แล้วจะใช้ตัวนั้นทันที ถ้ายังไม่มีจะดาวน์โหลดมาใช้ครั้งเดียวจบ เหมาะกับเครื่องมือที่ใช้แค่ตอนสร้างโปรเจกต์ครั้งแรก เครื่องหมาย -- คือตัวแบ่งระหว่าง "คำสั่งของ npm เอง" กับ "คำสั่งที่จะให้รันจริง" ต้องมีเว้นวรรคล้อมทั้งสองข้างเสมอ 💡 @nestjs/cli = เครื่องมือสร้างโครงโปรเจกต์ NestJS ให้อัตโนมัติ ไม่ต้องสร้างไฟล์เองทีละไฟล์ 💡 --skip-git = ไม่ต้องสร้าง Git repo ให้ (วิชานี้ไม่ใช้ Git) 💡 --skip-install = ยังไม่ต้องติดตั้ง package ตอนนี้ จะติดตั้งรวมทีเดียวจาก root ในขั้นถัดไป

✅ ต้องเห็น โฟลเดอร์ apps/api เกิดขึ้น มีไฟล์ src/main.ts, src/app.module.ts และอื่น ๆ

2.5 ติดตั้งทุกอย่างจาก root

กลับไปที่โฟลเดอร์บนสุดของโปรเจกต์

powershell
cd C:\webdev\booklab
npm install

ใช้เวลาสักครู่ (ยังไม่นานเท่า Section หน้าที่จะมี Angular เพิ่มเข้ามาด้วย)

💡 ทำไมต้องกลับมา install ที่ root อีกรอบ ตอนสร้างโปรเจกต์ NestJS เราสั่ง --skip-install ไว้ พอมาสั่ง npm install ที่ root ซึ่งรู้จัก apps/api ผ่าน workspaces ที่ตั้งไว้แล้ว npm จะติดตั้ง package ของ apps/api มารวมไว้ใน node_modules ที่ root เดียวกัน ทำให้ทุกโปรเจกต์ย่อยแชร์เครื่องมือชุดเดียวกันได้ ประหยัดพื้นที่กว่าแยกติดตั้งคนละที่มาก

✅ ต้องเห็น บรรทัดท้ายบอกจำนวน package ที่ติดตั้ง เช่น added 697 packages

2.6 เปิดใน VS Code

powershell
code .

💡 จุด . แปลว่า "โฟลเดอร์ที่เรายืนอยู่ตอนนี้"

ดูแถบด้านซ้ายใน VS Code จะเห็นโครงสร้างแบบนี้

text
booklab/
├── apps/
│   └── api/          ← NestJS (backend) — Section นี้ทำตรงนี้
├── node_modules/
├── package.json
└── package-lock.json

💡 frontend (หน้าบ้าน) กับ backend (หลังบ้าน) frontend คือส่วนที่ผู้ใช้เห็นและกดใช้งานได้โดยตรง (Angular ที่รันอยู่ในเบราว์เซอร์ — จะสร้างใน Section 3) backend คือส่วนที่ทำงานจริงอยู่เบื้องหลัง เก็บและจัดการข้อมูล (NestJS ที่ต่อกับฐานข้อมูล) ผู้ใช้ไม่เห็นส่วนนี้เลย

สำคัญ: สองตัวนี้เป็นโปรแกรมคนละตัว รันแยกกัน คุยกันด้วยการส่งข้อความไปกลับเท่านั้น ผู้ใช้แตะต้อง backend โดยตรงไม่ได้เลย

ต่อจากนี้ใบงานจะใช้คำว่า frontend และ backend ตลอด เพราะเป็นคำที่ใช้จริงในที่ทำงาน ในเอกสารทางการ และในผลค้นหาเวลาไปหาข้อมูลเพิ่มเอง

2.7 รัน backend ครั้งแรก

ใน VS Code กด Ctrl + ` เปิด Terminal แล้วพิมพ์

powershell
npm run dev:api

✅ ต้องเห็น

text
[Nest] LOG [NestApplication] Nest application successfully started

เปิดเบราว์เซอร์ที่ http://localhost:3000 ต้องเห็นคำว่า Hello World!

💡 localhost:3000 แปลว่าอะไร localhost = เครื่องตัวเอง ไม่ได้ออกอินเทอร์เน็ตเลย 3000 = port หรือเลขช่องทางเข้า เครื่องเดียวรันโปรแกรมได้หลายตัว จึงต้องแยกช่องกัน backend จองช่อง 3000 ส่วน frontend Angular จะใช้ช่อง 4200 ใน Section 3

⚠️ ปล่อย Terminal นี้รันทิ้งไว้ตลอด Section ห้ามปิด ถ้าต้องพิมพ์คำสั่งอื่น กดเครื่องหมาย + เปิดอันใหม่


Part 3 — เข้าใจ API ก่อนลงมือสร้าง

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • ⚪ Angular (frontend) (ยังไม่ถึง)
  • 🟢 NestJS (backend) ← กำลังทำส่วนนี้
  • ⚪ PostgreSQL (ฐานข้อมูล) (ยังไม่ถึง)

ต่อไป: ใช้ความเข้าใจนี้สร้าง API หนังสือตัวแรกที่คืนข้อมูลจริง

3.1 การเขียน API ทั่วไป

เวลาเปิดแอปสั่งอาหารหรือแอปพยากรณ์อากาศ สิ่งที่เห็นบนจอไม่ได้เก็บอยู่ในเครื่องเรา แต่ถูกดึงมาจากเซิร์ฟเวอร์อีกเครื่องหนึ่งผ่านอินเทอร์เน็ต API (Application Programming Interface) คือช่องทางกลางที่โปรแกรมหนึ่ง (เรียกว่า client เช่น มือถือ, เว็บเบราว์เซอร์) ใช้ขอข้อมูลหรือขอให้อีกโปรแกรมหนึ่ง (เรียกว่า server) ทำงานบางอย่างแทน โดย client ไม่ต้องรู้เลยว่าเบื้องหลัง server ทำงานอย่างไร รู้แค่ว่าจะขออะไร แล้วจะได้อะไรกลับมา

รูปแบบการทำงานพื้นฐานเรียกว่า request-response cycle

text
Client (browser/Angular)  --- Request --->  Server (API)
Client (browser/Angular)  <-- Response ---  Server (API)

💡 ทำไม frontend ต้องขอผ่าน API เสมอ ไม่แตะฐานข้อมูลตรง ๆ หน้าเว็บ Angular แตะฐานข้อมูลตรง ๆ ไม่ได้ ต้องขอผ่านช่องทางนี้เท่านั้น เพราะโค้ดที่รันในเบราว์เซอร์ใครก็เปิดอ่านได้ ถ้าเอารหัสฐานข้อมูลไปไว้ตรงนั้น เท่ากับแปะรหัสให้คนทั้งโลกดู (จะได้เห็นภาพจริงตอนต่อ Angular เข้ากับ API ใน Section 4)

3.2 ส่วนประกอบของ API

ทุกคำขอ (request) และคำตอบ (response) ของ API ประกอบด้วยส่วนเหล่านี้เสมอ

ส่วนประกอบ ความหมาย
EndpointURL เฉพาะจุดหนึ่งที่ตอบสนองคำขอเรื่องใดเรื่องหนึ่ง เช่น /books คือ endpoint ที่ตอบคำขอ "ขอรายชื่อหนังสือ"
Methodชนิดของคำขอ บอกว่า "จะให้ทำอะไรกับข้อมูล" (ดูตารางด้านล่าง)
Requestสิ่งที่ client ส่งไปหา server ประกอบด้วย endpoint + method และบางทีมีข้อมูลแนบไปด้วย (เรียกว่า body)
Responseสิ่งที่ server ส่งกลับมา ประกอบด้วย status code + ข้อมูล (body)

💡 HTTP Method มี 4 ชนิดหลักที่ใช้บ่อยที่สุด เวลาพิมพ์ URL ในเบราว์เซอร์ เบราว์เซอร์จะส่งคำขอชนิด GET ให้อัตโนมัติเสมอ ส่วนอีกสามชนิดจะได้ใช้ครบใน Section 5

Method ความหมาย
GETขอดูข้อมูล
POSTเพิ่มข้อมูลใหม่
PUTแก้ไขข้อมูลเดิม
DELETEลบข้อมูล

💡 Status Code คือรหัสตัวเลขที่ response ติดมาด้วยเสมอ บอกผลลัพธ์ของคำขอแบบไม่ต้องอ่าน body ก็รู้ทันที

Status Code ความหมาย
200สำเร็จ
400ข้อมูลที่ส่งมาผิด
404ไม่พบสิ่งที่ขอ (พิมพ์ URL ผิด หรือยังไม่มี endpoint นั้น)
500เซิร์ฟเวอร์มีปัญหาเอง

3.3 รูปแบบของ API (REST + JSON)

REST คือชุดข้อตกลงมาตรฐานในการออกแบบ endpoint ที่โลกจริงใช้กัน หลักการหลักคือตั้งชื่อ endpoint ด้วยคำนามแทนการบอกการกระทำ แล้วให้ HTTP Method เป็นตัวบอกว่าจะทำอะไร เช่น แทนที่จะเขียน /getBooks ให้เขียน /books เฉย ๆ แล้วส่งเป็น GET

💡 JSON (JavaScript Object Notation) = data format แบบ text สำหรับส่งข้อมูลผ่าน HTTP ระหว่าง client กับ server เขียนเป็น key-value pair คั่นด้วย : ครอบด้วย {} ไวยากรณ์อ้างอิงมาจาก object literal ของ JavaScript แต่ตัวมันเองเป็นแค่ string จนกว่าจะถูก parse ให้กลายเป็น object จริง เป็นรูปแบบมาตรฐานที่ API เกือบทุกที่ในโลกใช้ตอบกลับ

3.4 โครงสร้างของ NestJS API

ตอนสร้างโปรเจกต์ใน 2.4 NestJS สร้างไฟล์มาให้ชุดหนึ่งแล้ว มาดูกันว่าแต่ละไฟล์ทำหน้าที่อะไร เรียงตามลำดับที่โปรแกรมทำงานจริง

ไฟล์ หน้าที่
main.tsจุดเริ่มต้นโปรแกรม (entry point) — เรียก NestFactory.create() สร้างแอปขึ้นมา แล้ว app.listen() เปิด port รอรับคำขอ เป็นไฟล์แรกที่ Node.js รันเมื่อสั่ง npm run dev:api
app.module.tsroot module — รวมทุกส่วนของโปรแกรมเข้าด้วยกัน บอก NestJS ว่ามี controller และ provider (service) อะไรบ้าง
app.controller.tsรับคำขอ (request) จาก client แล้วส่งต่อให้ service ประมวลผล ไม่ควรมี logic ซับซ้อนอยู่ในนี้
app.service.tsทำงานจริง (business logic) เตรียมข้อมูลส่งกลับให้ controller

💡 ลำดับการทำงานเมื่อมี request เข้ามา: main.ts บูตแอปขึ้นมา → request วิ่งเข้า app.controller.ts → controller เรียก app.service.ts ให้ทำงานจริง → service คืนผลลัพธ์กลับให้ controller → controller ส่ง response กลับไปหา client

3.5 Method GET พ่น Hello World

ใน 2.7 ตอนเปิด http://localhost:3000 แล้วเห็นคำว่า Hello World! นั่นมาจากโค้ดที่ NestJS สร้างไว้ให้ตั้งแต่ต้น เปิดดูสองไฟล์นี้

apps/api/src/app.controller.ts

typescript
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();
  }
}

apps/api/src/app.service.ts

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

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

💡 ไล่โค้ดทีละบรรทัด

  • @Controller() ไม่มี argument = ผูกกับ path ว่าง ๆ (root) พอรวมกับที่ยังไม่ได้ตั้ง prefix ใด ๆ จึงตอบที่ / ตรง ๆ
  • constructor(private readonly appService: AppService) = ขอให้ NestJS ส่ง AppService มาให้ใช้ (dependency injection) ไม่ต้องสร้างเองด้วย new
  • @Get() = ผูก method getHello เข้ากับ HTTP method GET
  • getHello(): string = ประกาศว่า method นี้คืนค่าเป็น string เท่านั้น
  • return this.appService.getHello(); = controller ไม่ได้เก็บข้อความ "Hello World!" เอง แต่เรียก service ให้ไปหยิบมาให้ ตรงตามหน้าที่ที่แบ่งไว้ในตาราง 3.4

วิธีดูผลบน Browser — ต้อง รัน backend ก่อน ถึงจะเปิดดูได้ ถ้า Terminal ที่รัน npm run dev:api จาก 2.7 ยังเปิดค้างอยู่ก็ใช้ได้เลย แต่ถ้าปิดไปแล้วให้เปิด Terminal ใหม่แล้วรัน

powershell
cd C:\webdev\booklab
npm run dev:api

จากนั้นเปิด http://localhost:3000 เบราว์เซอร์จะส่งคำขอ GET / ให้อัตโนมัติ (ตามที่อธิบายไว้ใน 3.2) วิ่งไปเข้า getHello() แล้วได้ข้อความ Hello World! กลับมาแสดงบนจอ

3.6 ตัวแปรใน NestJS + ลองพ่นข้อมูลของตัวเอง

ภายใน method ของ controller หรือ service ประกาศตัวแปร TypeScript ธรรมดาได้เลย แล้ว return ออกไป NestJS จะแปลงเป็น response ให้อัตโนมัติ ลองเพิ่ม method ใหม่ใน app.controller.ts

typescript
@Get('me')
getMe(): string {
  const name = 'สมชาย';
  const section = 1;
  return `สวัสดี ${name} จาก Section ${section}`;
}

💡 @Get('me') = มี argument รอบนี้ จึงผูกกับ path /me ต่อท้าย base path ของ controller (ต่างจาก @Get() เปล่า ๆ ใน 3.5 ที่ผูกกับ path ว่าง) 💡 Template literal `...${name}...` = สร้างข้อความที่แทรกค่าตัวแปรเข้าไปได้ ต้องใช้ backtick (`) ครอบ ไม่ใช่เครื่องหมายคำพูดธรรมดา

✅ ต้องเห็น เปิด http://localhost:3000/me แล้วเจอข้อความที่ประกอบจากตัวแปร

text
สวัสดี สมชาย จาก Section 1

🚀 ทำเอง — เปลี่ยนค่าตัวแปร name เป็นชื่อตัวเอง เพิ่มตัวแปรใหม่เก็บชื่อเครื่องที่นั่งอยู่ แล้วต่อเข้าไปในข้อความที่ return ด้วย


Part 4 — สร้าง API ตัวแรก

🗺️ ตำแหน่งตอนนี้ในระบบ

  • ✅ เตรียมเครื่องมือ (ทำไปแล้ว)
  • ⚪ Angular (frontend) (ยังไม่ถึง)
  • 🟢 NestJS (backend) ← กำลังทำส่วนนี้
  • ⚪ PostgreSQL (ฐานข้อมูล) (ยังไม่ถึง)

ต่อไป: Section หน้า: ต่อ API เข้ากับฐานข้อมูลจริง (PostgreSQL)

เอาโครงสร้าง main.ts → module → controller → service จากตาราง 3.4 มาใช้จริง แต่คราวนี้สร้างชุดไฟล์แยกเฉพาะเรื่องหนังสือ (BooksModule, BooksController) แทนที่จะยัดทุกอย่างไว้ใน AppModule/AppController ที่ดูแลทั้งแอป และแทนที่จะตอบข้อความตายตัวแบบ getHello() ใน 3.5 จะสร้าง endpoint ที่ตอบข้อมูลหนังสือทั้งชุด

4.1 เปิดใช้งาน CORS และตั้งให้ทุก endpoint ขึ้นต้นด้วย /api

ตอนนี้ยังไม่มี frontend ให้ทดสอบ (จะสร้างใน Section 3) แต่ตั้งค่านี้ไว้ล่วงหน้าเลย จะได้ไม่ต้องย้อนมาแก้ไฟล์นี้อีกทีตอนนั้น เปิดไฟล์ apps/api/src/main.ts แก้ให้เป็นแบบนี้

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  app.setGlobalPrefix('api');
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

💡 app.enableCors() = เปิดใช้งาน CORS (Cross-Origin Resource Sharing) เพื่ออนุญาตให้ frontend Angular (ที่รันบน port 4200) สามารถยิงคำขอ HTTP มาดึงข้อมูลจาก backend NestJS (ที่รันบน port 3000) ได้โดยตรงโดยไม่ถูกเบราว์เซอร์บล็อก

💡 app.setGlobalPrefix('api') = สั่งให้ NestJS เติมคำว่า /api ไว้หน้า URL ของทุก endpoint โดยอัตโนมัติ ไม่ต้องพิมพ์เองทุกจุด การรวม endpoint ไว้ใต้ /api ช่วยให้แยกเส้นทางระหว่าง API กับ frontend ได้อย่างชัดเจน

ดู Terminal ที่รัน npm run dev:api จะเห็นเซิร์ฟเวอร์รีสตาร์ทเองอัตโนมัติ แล้วลองเปิด http://localhost:3000/api/ ต้องเห็น Hello World! เหมือนเดิม (แค่ URL เปลี่ยนไปมี /api นำหน้า)

4.2 สร้าง Books Module

เปิด Terminal ใหม่ (อย่าปิดตัวที่รันอยู่)

powershell
cd apps/api
npm exec -- nest g module books
npm exec -- nest g controller books --no-spec

💡 books.module.ts และ books.controller.ts ทำหน้าที่แบบเดียวกับ app.module.ts และ app.controller.ts ในตาราง 3.4 เป๊ะ ต่างกันแค่ขอบเขต — AppModule/AppController ดูแลทั้งแอป ส่วน BooksModule/BooksController แยกออกมาดูแลเฉพาะเรื่องหนังสือ ไม่ปนกับเรื่องอื่น เวลาโปรเจกต์ใหญ่ขึ้นจะได้ไม่ต้องยัดทุกอย่างไว้ไฟล์เดียว

💡 nest g — g ย่อจาก generate สั่งให้สร้างไฟล์โครงร่างให้อัตโนมัติ ประหยัดเวลากว่าสร้างไฟล์เองทีละไฟล์แบบตอนสร้าง app.controller.ts ที่ CLI ทำให้ตั้งแต่ต้น

✅ ต้องเห็น โฟลเดอร์ apps/api/src/books/ มีไฟล์ books.module.ts และ books.controller.ts

4.3 สร้าง Book Interface

สร้างไฟล์ apps/api/src/books/book.interface.ts

typescript
export interface Book {
  id: number;
  title: string;
  author: string;
  year: number;
}

💡 interface = ไวยากรณ์ของ TypeScript สำหรับกำหนด shape ของข้อมูล กำหนดว่าหนังสือหนึ่งเล่มต้องมี property อะไรบ้าง ชนิดใด ตัว interface เองไม่เก็บข้อมูลจริง เป็นแค่กติกาให้ TypeScript ตรวจสอบตอนคอมไพล์ — วัตถุ (object) ที่ประกาศเป็นชนิด Book ต้องมีครบทุก property ตามนี้ ไม่งั้น TypeScript แจ้ง error ทันที

💡 export = เปิดให้ไฟล์อื่นเรียกใช้ได้ ถ้าไม่ใส่ ของจะอยู่แค่ในไฟล์ตัวเอง

4.4 เขียน controller

แทนที่ทั้งไฟล์ apps/api/src/books/books.controller.ts

typescript
import { Controller, Get } from '@nestjs/common';
import { Book } from './book.interface';

const BOOKS: Book[] = [
  { id: 1, title: 'Clean Code', author: 'Robert C. Martin', year: 2008 },
  { id: 2, title: 'The Pragmatic Programmer', author: 'Andy Hunt', year: 1999 },
  { id: 3, title: 'Refactoring', author: 'Martin Fowler', year: 1999 },
];

@Controller('books')
export class BooksController {
  @Get()
  findAll(): Book[] {
    return BOOKS;
  }
}

💡 @Controller() และ @Get() ตัวนี้ทำหน้าที่แบบเดียวกับที่ไล่โค้ดไปแล้วใน 3.5 เป๊ะ (เรียกรวม ๆ ว่า decorator — syntax ที่แนบ metadata ให้ class หรือ method ให้ NestJS อ่านตอน runtime ว่าจะ route request เข้ามาที่ไหน) ต่างจาก 3.5 อยู่จุดเดียวคือรอบนี้ @Controller('books') มี argument — ตามหลัก REST จาก 3.3 ที่ตั้งชื่อ endpoint ด้วยคำนามพหูพจน์ ทำให้ทุก route ในไฟล์นี้ผูกกับ path prefix books รวมกับ global prefix /api จาก 4.1 กลายเป็น /api/books

ส่วนของโค้ด ความหมายเชิงเทคนิค
findAll(): Book[]return type annotation — TypeScript ตรวจตอน compile ว่า return value ต้องเป็น array ที่มี shape ตรงตาม interface Book จาก 4.3
return BOOKS;NestJS serialize return value เป็น JSON response ให้อัตโนมัติผ่าน HTTP adapter (Express) ไม่ต้องเรียก res.json() เอง — ตรงกับรูปแบบ JSON ที่อธิบายไว้ใน 3.3
import { Book } from './book.interface'ES module import — ดึง type มาใช้ตรวจสอบตอน compile เท่านั้น interface ไม่มีอยู่จริงตอน runtime (type erasure)

4.5 ทดสอบ

เปิดเบราว์เซอร์ที่ http://localhost:3000/api/books

✅ ต้องเห็นข้อมูลหนังสือ 3 เล่ม หน้าตาแบบนี้

json
[{"id":1,"title":"Clean Code","author":"Robert C. Martin","year":2008}, ...]

ตรงตามรูปแบบ JSON และ status code 200 ที่อธิบายไว้ใน 3.2–3.3

❌ ถ้าขึ้น 404 (ความหมายตามตาราง 3.2) → ดู Terminal ว่ามีข้อความสีแดงไหม แล้วเช็กว่า BooksModule ถูกเรียกใช้ใน app.module.ts แล้วหรือยัง

4.6 ทำเอง — เพิ่ม endpoint ที่สอง

ทำให้ http://localhost:3000/api/books/count ตอบกลับว่า

json
{ "total": 3 }

คำใบ้: ใช้ @Get('count') แล้วคืน object ธรรมดา


🚀 ถ้าเสร็จก่อนเพื่อน

  1. เพิ่ม endpoint /api/books/newest คืนเฉพาะหนังสือปีใหม่ที่สุด
  2. เพิ่มช่อง publisher ใน interface Book แล้วดูว่า TypeScript เตือนที่ไหนบ้าง
  3. เปลี่ยน @Get() เป็น @Post() แล้วเปิด URL เดิม — ทำไมหาไม่เจอ?

❓ ปัญหาที่พบบ่อย

อาการ สาเหตุ วิธีแก้
'npm' is not recognized เปิด PowerShell ก่อนติดตั้ง Node เสร็จ ปิดแล้วเปิดใหม่
node -v ขึ้น v22 ติดตั้งผิดเวอร์ชัน ถอนแล้วติดตั้ง Node 24 LTS
EADDRINUSE: port 3000 มี Terminal เก่ารันค้าง แย่งช่อง 3000 ปิด Terminal เก่า หรือรีสตาร์ทเครื่อง
แก้โค้ดแล้วไม่เปลี่ยน ลืมกดบันทึก Ctrl + S แล้วดู Terminal ว่าโหลดใหม่
เส้นแดงเต็มไปหมด npm install ยังไม่เสร็จ รอให้เสร็จ แล้วปิด-เปิด VS Code

📌 การบ้านก่อน Section หน้า

ทบทวนวิชาฐานข้อมูล: primary key, foreign key, ความสัมพันธ์ one-to-many — Section หน้าจะออกแบบตารางจริงบน PostgreSQL แล้วให้ API ดึงจากฐานข้อมูลจริงแทน array ที่เขียนไว้ในโค้ด

💡 สรุปสั้น ๆ

  • primary key = ช่องที่ระบุตัวตนของแต่ละแถวแบบไม่ซ้ำกันเลยในตาราง เหมือนเลขบัตรประชาชน หนึ่งคนมีเลขเดียว ไม่มีใครซ้ำ ใน Section หน้าจะเจอเป็นช่อง id ที่ฐานข้อมูลแจกเลขให้เองอัตโนมัติ
  • foreign key = ช่องในตารางหนึ่งที่ไปชี้ถึง primary key ของอีกตาราง ใช้บอกว่าแถวนี้ "เป็นของ" หรือ "เกี่ยวข้องกับ" แถวไหนในอีกตาราง เช่น ช่อง categoryId ในตารางหนังสือที่ชี้ไปหา id ของตารางหมวดหมู่
  • ความสัมพันธ์ one-to-many = "หนึ่งต่อหลาย" คือของฝั่งหนึ่งมีได้แค่ 1 แต่โยงไปหาอีกฝั่งได้หลายรายการ เช่น หมวดหมู่ 1 หมวด มีหนังสืออยู่ในนั้นได้หลายเล่ม แต่หนังสือ 1 เล่มอยู่ได้แค่หมวดหมู่เดียว

ไม่ต้องเข้าใจลึกตอนนี้ก็ได้ Section หน้าจะได้ลงมือสร้างตารางจริงแล้วจะเห็นภาพชัดเจนขึ้นมาก