Section 1

ตั้งค่าเครื่องมือ + สร้าง API ตัวแรก
⚠️ อ่านก่อนเริ่ม
- จำหมายเลขเครื่องของตัวเอง แล้วนั่งเครื่องเดิมทุก 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 ต้องอาศัยตัวนี้ทำงาน
- เปิดเบราว์เซอร์ไปที่ nodejs.org
- หน้าแรกจะมีปุ่มดาวน์โหลดสองปุ่ม ให้กดปุ่มที่เขียนว่า LTS (มีคำว่า Recommended for Most Users กำกับอยู่) ห้ามกดปุ่ม Current แม้จะแสดงเลขเวอร์ชันสูงกว่าก็ตาม เพราะ Current คือรุ่นทดลองที่ยังไม่เสถียร
- ไฟล์ที่ได้จะชื่อประมาณ
node-v24.x.x-x64.msi(ลงท้ายด้วย x64 แปลว่าเป็นรุ่น 64 บิต ซึ่งตรงกับเครื่องแลปเกือบทุกเครื่อง) - ดับเบิลคลิกไฟล์ที่ดาวน์โหลดมาเพื่อเริ่มติดตั้ง
ระหว่างติดตั้ง จะเจอหน้าจอเรียงกันแบบนี้ ให้ทำตาม:
| หน้าจอ | ต้องทำอะไร |
|---|---|
| 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
ทำไมต้องมี: เป็นโปรแกรมที่เราใช้พิมพ์โค้ดตลอดทั้งเทอม
- เปิดเบราว์เซอร์ไปที่ code.visualstudio.com
- กดปุ่มดาวน์โหลดสีน้ำเงินตรงกลางหน้า (จะดาวน์โหลดตัวติดตั้งของ Windows ให้อัตโนมัติ)
- ดับเบิลคลิกไฟล์ที่ได้ (ชื่อประมาณ
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 คือหน้าจอที่เราพิมพ์คำสั่งสั่งงานคอมพิวเตอร์ตรง ๆ แทนการคลิกเมาส์ นักพัฒนาใช้ตลอดเวลาเพราะเร็วกว่า
node -v
npm -v
code --version
✅ ต้องเห็น
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 สร้างโครงโปรเจกต์
cd C:\
mkdir webdev
cd webdev
mkdir booklab
cd booklab
npm init -y
💡
npm init -y= สร้างไฟล์package.jsonเปล่า ๆ ให้อัตโนมัติ (ตัว-yคือตอบ "ใช่" ทุกคำถามแทนเรา) ไฟล์นี้คือบัตรประจำตัวของโปรเจกต์ บอกว่าโปรเจกต์ชื่ออะไร ใช้เครื่องมืออะไรบ้าง
✅ ต้องเห็นไฟล์ package.json เปิดดูจะมีเนื้อหาประมาณนี้
{
"name": "booklab",
"version": "1.0.0",
...
}
2.2 ตั้งค่าให้เป็น monorepo
โปรเจกต์เราจะมีทั้ง backend (NestJS) และ frontend (Angular) อยู่ในที่เดียวกัน แบบนี้เรียกว่า monorepo (repo เดียว หลายโปรเจกต์ย่อยข้างใน)

เปิดไฟล์ package.json ด้วย VS Code (code . เปิดทั้งโฟลเดอร์ก็ได้) แล้วแก้ให้เป็นแบบนี้
{
"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 ติดตั้งเครื่องมือช่วยรันสองฝั่งพร้อมกัน
npm install --save-dev concurrently
💡
concurrently= เครื่องมือที่รันหลายคำสั่งพร้อมกันใน Terminal เดียว เราจะใช้ตอนอยากรันทั้ง backend และ frontend พร้อมกันด้วยคำสั่งnpm run dev(สร้างไว้ให้แล้วในขั้นที่แล้ว) แต่วันนี้ยังไม่ได้ใช้ เพราะมีแค่ backend
✅ ต้องเห็น โฟลเดอร์ node_modules และไฟล์ package-lock.json เกิดขึ้น
2.4 สร้างโปรเจกต์ NestJS (backend)
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
กลับไปที่โฟลเดอร์บนสุดของโปรเจกต์
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
code .
💡 จุด
.แปลว่า "โฟลเดอร์ที่เรายืนอยู่ตอนนี้"
ดูแถบด้านซ้ายใน VS Code จะเห็นโครงสร้างแบบนี้
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 แล้วพิมพ์
npm run dev:api
✅ ต้องเห็น
[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
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 ประกอบด้วยส่วนเหล่านี้เสมอ
| ส่วนประกอบ | ความหมาย |
|---|---|
| Endpoint | URL เฉพาะจุดหนึ่งที่ตอบสนองคำขอเรื่องใดเรื่องหนึ่ง เช่น /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.ts | root 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
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
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()= ผูก methodgetHelloเข้ากับ HTTP methodGETgetHello(): string= ประกาศว่า method นี้คืนค่าเป็น string เท่านั้นreturn this.appService.getHello();= controller ไม่ได้เก็บข้อความ "Hello World!" เอง แต่เรียก service ให้ไปหยิบมาให้ ตรงตามหน้าที่ที่แบ่งไว้ในตาราง 3.4
วิธีดูผลบน Browser — ต้อง รัน backend ก่อน ถึงจะเปิดดูได้ ถ้า Terminal ที่รัน npm run dev:api จาก 2.7 ยังเปิดค้างอยู่ก็ใช้ได้เลย แต่ถ้าปิดไปแล้วให้เปิด Terminal ใหม่แล้วรัน
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
@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 แล้วเจอข้อความที่ประกอบจากตัวแปร
สวัสดี สมชาย จาก 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 แก้ให้เป็นแบบนี้
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 ใหม่ (อย่าปิดตัวที่รันอยู่)
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
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
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 prefixbooksรวมกับ global prefix/apiจาก 4.1 กลายเป็น/api/books
ส่วนของโค้ด ความหมายเชิงเทคนิค findAll(): Book[]return type annotation — TypeScript ตรวจตอน compile ว่า return value ต้องเป็น array ที่มี shape ตรงตาม interface Bookจาก 4.3return BOOKS;NestJS serialize return value เป็น JSON response ให้อัตโนมัติผ่าน HTTP adapter (Express) ไม่ต้องเรียก res.json()เอง — ตรงกับรูปแบบ JSON ที่อธิบายไว้ใน 3.3import { Book } from './book.interface'ES module import — ดึง type มาใช้ตรวจสอบตอน compile เท่านั้น interface ไม่มีอยู่จริงตอน runtime (type erasure)
4.5 ทดสอบ
เปิดเบราว์เซอร์ที่ http://localhost:3000/api/books
✅ ต้องเห็นข้อมูลหนังสือ 3 เล่ม หน้าตาแบบนี้
[{"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 ตอบกลับว่า
{ "total": 3 }
คำใบ้: ใช้ @Get('count') แล้วคืน object ธรรมดา
🚀 ถ้าเสร็จก่อนเพื่อน
- เพิ่ม endpoint
/api/books/newestคืนเฉพาะหนังสือปีใหม่ที่สุด - เพิ่มช่อง
publisherใน interfaceBookแล้วดูว่า TypeScript เตือนที่ไหนบ้าง - เปลี่ยน
@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 หน้าจะได้ลงมือสร้างตารางจริงแล้วจะเห็นภาพชัดเจนขึ้นมาก