📘 Booklab Workshop

Day 1

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

เวลา: 120 นาที จบคาบนี้จะได้: เปิด http://localhost:3000/api/books แล้วเห็นข้อมูลหนังสือ


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

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

ช่วงที่ 1 — เตรียมเครื่อง (25 นาที)

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

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

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

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

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

ทำไมต้องมี: เป็นโปรแกรมที่ทำให้รันภาษา JavaScript บนเครื่องเราได้ ปกติ JavaScript รันได้แต่ในเบราว์เซอร์ Node.js ทำให้มันรันเป็นโปรแกรมเดี่ยว ๆ บนเครื่องได้ ทั้ง 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 ในคาบที่ 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"


ช่วงที่ 2 — เปิดโปรเจกต์ (25 นาที)

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

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

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

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

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

Full-Stack Monorepo Architecture

เปิดไฟล์ 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 เดียว เราจะใช้ตอนอยากรันทั้งหลังบ้านและหน้าบ้านพร้อมกันด้วยคำสั่ง npm run dev (สร้างไว้ให้แล้วในขั้นที่แล้ว) แต่วันนี้ยังไม่ได้ใช้ เพราะมีแค่หลังบ้าน

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

2.4 สร้างโปรเจกต์ NestJS (หลังบ้าน)

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

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

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

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

2.6 เปิดใน VS Code

powershell
code .

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

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

text
booklab/
├── apps/
│   └── api/          ← NestJS (หลังบ้าน) — คาบนี้ทำตรงนี้
├── node_modules/
├── package.json
└── package-lock.json

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

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

2.7 รันหลังบ้านครั้งแรก

ใน 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 หรือเลขช่องทางเข้า เครื่องเดียวรันโปรแกรมได้หลายตัว จึงต้องแยกช่องกัน หลังบ้านจองช่อง 3000 ส่วนหน้าบ้าน Angular จะใช้ช่อง 4200 ในคาบที่ 3

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


ช่วงที่ 3 — TypeScript เท่าที่ต้องใช้ (25 นาที)

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

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

ต่อไป: เขียน API ตัวแรกด้วยความรู้ TypeScript ที่เพิ่งฝึก

💡 TypeScript คืออะไร คือ JavaScript ที่เพิ่มความสามารถหนึ่งอย่าง คือบอกล่วงหน้าได้ว่าตัวแปรแต่ละตัวเก็บข้อมูลชนิดไหน พอเผลอใส่ผิดชนิด จะขีดเส้นแดงเตือนตั้งแต่ตอนพิมพ์ ไม่ต้องรอให้พังตอนผู้ใช้กดใช้จริง

3.1 สร้างไฟล์ทดลอง

สร้างไฟล์ apps/api/src/playground.ts แล้วพิมพ์เอง (อย่าคัดลอก จะได้เห็นว่า VS Code เตือนตรงไหน)

typescript
let title: string = 'Clean Code';
let year: number = 2008;
let available: boolean = true;

💡 : string คือป้ายบอกว่าช่องนี้ใส่ได้แต่ข้อความ ลองเปลี่ยน title เป็นตัวเลขดู แล้วสังเกตเส้นแดง ชนิดที่ใช้บ่อย: string ข้อความ, number ตัวเลข, boolean จริง/เท็จ

พิมพ์ต่อ

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

💡 interface คือแบบฟอร์มเปล่า กำหนดว่าหนังสือหนึ่งเล่มต้องมีช่องอะไรบ้าง ใครจะสร้างหนังสือต้องกรอกครบทั้งสี่ช่อง ขาดช่องเดียวไม่ผ่าน — ตัว interface เองไม่เก็บข้อมูล เป็นแค่กติกา

typescript
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 },
];

console.log(books);

💡 Book[] วงเล็บเหลี่ยมแปลว่า "หลายอัน" — รายการที่มีหนังสือหลายเล่ม ทุกเล่มต้องกรอกตามแบบฟอร์ม Book

3.2 ทดลองทำให้มันพัง

typescript
books.push({ id: 3, title: 'Refactoring' });

✅ ต้องเห็น เส้นหยักแดง บอกว่าขาด author กับ year

นี่คือประโยชน์หลักของ TypeScript — ถ้าเป็น JavaScript ธรรมดาบรรทัดนี้จะผ่านฉลุย แล้วไปพังตอนผู้ใช้จริงเปิดหน้าเว็บ ซึ่งกว่าจะรู้ก็สายไปแล้ว

ลบบรรทัดนี้ออกก่อนไปต่อ

3.3 งานที่ต้องรอ

typescript
async function getBooks(): Promise<Book[]> {
  return books;
}

getBooks().then((result) => console.log('จำนวนหนังสือ:', result.length));

💡 async / Promise / await งานบางอย่างต้องรอ เช่นไปหยิบข้อมูลจากฐานข้อมูล เหมือนกดสั่งซื้อของออนไลน์แล้วต้องรอของมาส่งถึงก่อนถึงจะแกะดูได้

  • async = ป้ายบอกว่า "ฟังก์ชันนี้เป็นงานที่ต้องรอ"
  • Promise<Book[]> = ใบจอง ยังไม่ใช่หนังสือจริง แต่สัญญาว่าเดี๋ยวจะได้รายการหนังสือแน่นอน
  • await = "หยุดตรงนี้ รอของมาถึงก่อน" (จะใช้เยอะในคาบหน้า)

ทุกครั้งที่โค้ดคุยกับฐานข้อมูลหรือ API จะเจอสามคำนี้เสมอ


ช่วงที่ 4 — สร้าง API ตัวแรก (35 นาที)

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

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

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

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

💡 endpoint = URL เฉพาะจุดหนึ่งที่ตอบสนองคำขอเรื่องใดเรื่องหนึ่ง /api/books คือ endpoint ที่ตอบคำขอ "ขอรายชื่อหนังสือ"

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

เปิดไฟล์ 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) เพื่ออนุญาตให้หน้าบ้าน Angular (ที่รันบน port 4200) สามารถยิงคำขอ HTTP มาดึงข้อมูลจากหลังบ้าน NestJS (ที่รันบน port 3000) ได้โดยตรงโดยไม่ถูกเบราว์เซอร์บล็อก

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

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

4.2 สร้างแผนกหนังสือ

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

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

💡 module = แผนก เก็บทุกอย่างเรื่องหนังสือไว้ด้วยกัน ไม่ปนแผนกอื่น 💡 controller = พนักงานต้อนรับของแผนก รับคำขอจากลูกค้าแล้วตอบกลับ 💡 nest gg ย่อจาก generate สั่งให้สร้างไฟล์โครงร่างให้อัตโนมัติ

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

4.3 สร้างแบบฟอร์มหนังสือ

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

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

💡 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;
  }
}

💡 เครื่องหมาย @ (decorator) = ป้ายที่แปะไว้บนหัว บอกว่าสิ่งนี้ทำหน้าที่อะไร

  • @Controller('books') = "ทุกคำขอที่ขึ้นต้นด้วย /books มาหาฉัน" (แล้วรวมกับ /api ที่ตั้งไว้ในขั้นที่ 4.1 กลายเป็น /api/books)
  • @Get() = "ฟังก์ชันนี้ตอบคนที่มาขอดูข้อมูล"

💡 import = เรียกของจากไฟล์อื่นมาใช้ 💡 : Book[] ท้ายชื่อฟังก์ชัน = สัญญาว่าจะคืนรายการหนังสือเท่านั้น

4.5 ทดสอบ

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

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

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

💡 JSON = รูปแบบการเขียนข้อมูลให้โปรแกรมส่งหากันรู้เรื่อง เขียนเป็นคู่ "ชื่อช่อง: ค่า" ในวงเล็บปีกกา หน้าตาเหมือน object ใน JavaScript

💡 GET = เวลาเราพิมพ์ URL ในเบราว์เซอร์ มันส่งคำขอชนิด GET แปลว่า "ขอดูข้อมูล" เสมอ ยังมีอีกสามชนิดคือ POST เพิ่มใหม่, PUT แก้ไข, DELETE ลบ — จะได้ใช้ครบในคาบที่ 5

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

💡 404 = รหัสที่เซิร์ฟเวอร์ตอบว่า "ไม่เจอสิ่งที่ขอ" มักเกิดจากพิมพ์ URL ผิด หรือยังไม่ได้สร้าง endpoint นั้น

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

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

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

💡 สรุปสั้น ๆ

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

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