โหมดมืด
บทที่ 11 — Production + Modern Node: เอาขึ้นจริง + ก้าวต่อไป
บทสุดท้าย — เปลี่ยน "โค้ดที่รันในเครื่องเรา" ให้เป็น "บริการที่รันบน production ได้จริง 24 ชั่วโมง" แล้วปิดท้ายด้วยแผนที่ก้าวต่อไป:
- Docker สำหรับ Node — image ที่เล็ก ปลอดภัย เร็ว
- Process manager (PM2) + การรันใน container
- Health check + structured logging (pino)
- Security checklist — กับดักที่ทำระบบโดนแฮก
- Modern Node — ฟีเจอร์ใหม่ที่ควรรู้ + TypeScript กับ Node + Deno/Bun
- Roadmap — ไปต่อที่ไหน (NestJS, Next.js, ...)
1. Docker สำหรับ Node — image เล็กและปลอดภัย
Production ส่วนใหญ่รัน Node ใน container (ดู Docker) จุดสำคัญคือทำ image ให้เล็ก + ปลอดภัย ด้วย multi-stage build:
📘 คำศัพท์ Dockerfile แบบเร็ว ๆ (รายละเอียดที่ Docker):
FROM ... AS <name>= เริ่ม stage ใหม่จาก image ฐาน + ตั้งชื่อให้อ้างถึงได้WORKDIR=cdเข้า folder นั้น (ถ้าไม่มีจะสร้างให้)COPY --chown=user:group src dest= copy ไฟล์เข้า image แล้วเปลี่ยนเจ้าของEXPOSE= บอกว่า container เปิด port อะไร (เอกสารเฉย ๆ — ไม่ได้เปิดให้จริง)CMD= คำสั่งที่ container รันตอน start
dockerfile
# Dockerfile — multi-stage: stage แรก build, stage สองเอาเฉพาะของจำเป็น
# (ตัวอย่างนี้สำหรับ pure-JS — ถ้าเป็น TypeScript ดู stage 'build' ด้านล่าง)
# ---- stage 1: ติดตั้ง dependencies ----
# 💡 production แนะนำ pin ด้วย digest เช่น node:22-slim@sha256:... เพื่อกัน image เปลี่ยน
FROM node:22-slim AS deps
WORKDIR /app
COPY package*.json ./
# npm ci = ติดตั้งจาก lockfile เป๊ะ (บทที่ 1) · --omit=dev = ไม่เอา devDependencies → เล็กลง
RUN npm ci --omit=dev
# ---- stage 2: image จริงที่ deploy (เล็ก ไม่มีเครื่องมือ build) ----
FROM node:22-slim
WORKDIR /app
ENV NODE_ENV=production
# ⭐ รันด้วย user ที่ไม่ใช่ root (image node มี user 'node' ให้แล้ว) — ปลอดภัยขึ้น
USER node
COPY --chown=node:node --from=deps /app/node_modules ./node_modules
COPY --chown=node:node . .
EXPOSE 3000
# HEALTHCHECK = ให้ Docker เช็กว่า container ยังไหวไหม (ใน K8s ใช้ probe แทน — ข้อ 3)
# บรรทัดล่างนี้คือ: เรียก /healthz → ถ้าตอบ ok ก็ exit(0) แปลว่า healthy
# → ถ้าไม่ตอบ/error ก็ exit(1) แปลว่า unhealthy
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:3000/healthz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
# รัน node ตรง ๆ (ไม่ผ่าน npm) เพื่อให้ signal SIGTERM ส่งถึง process จริง (บทที่ 7)
# ไม่ต้องใช้ dumb-init/tini เพราะแอปนี้ไม่ spawn child process ที่ต้องการ zombie reaping
# และ CMD ใช้ exec form (ไม่ใช่ shell form) — signal จึงส่งถึง Node ตรง ๆ อยู่แล้ว
# (ถ้าแอป spawn subprocess ด้วย execFile/spawn — บทที่ 10 — ให้พิจารณาใช้ tini อยู่ดี)
CMD ["node", "server.mjs"]💡 TypeScript / มี build step? ใช้ 3 stage:
deps(npm ci ทุก dep รวม dev),build(tscหรือ esbuild → dist/),runtime(npm ci --omit=dev+ copydist/จาก stage build) — stage runtime จะไม่มี TypeScript compiler ติดมาด้วย image จะเล็กและปลอดภัยกว่า
bash
# .dockerignore — อย่า copy ของพวกนี้เข้า image (สำคัญ!)
node_modules
.env
.git
*.logจุดที่มือใหม่พลาดบ่อย:
COPY package*.jsonก่อนCOPY . .— เพื่อให้ Docker cache ชั้นnpm ciไว้ (โค้ดเปลี่ยนแต่ dependency ไม่เปลี่ยน → ไม่ต้องติดตั้งใหม่ build เร็วขึ้นมาก)node:22-slim(หรือalpine) แทนnode:22เต็ม — เล็กกว่าหลายร้อย MB--omit=dev— ไม่เอา devDependencies ขึ้น production- ใส่
.envใน.dockerignore— ไม่ฝังความลับใน image (ส่ง env ตอน run แทน — บทที่ 7)
⚠️ Pitfall: ใช้
CMD ["npm", "start"]แทนCMD ["node", "server.mjs"]→ npm กลายเป็น process หลัก (PID 1) และ ไม่ส่งต่อ SIGTERM ให้ node → graceful shutdown (บทที่ 7) ไม่ทำงาน · รันnodeตรง ๆ
2. Process manager + การรันใน container
โปรแกรมต้อง "ฟื้นเองเมื่อ crash" และใช้ทุก CPU core (บทที่ 10) — มี 2 แนวทางตามที่ deploy:
บน VM/เครื่องเปล่า → PM2:
bash
npm install -g pm2
pm2 start server.mjs -i max # -i max = cluster ตามจำนวน core (บทที่ 10)
pm2 startup && pm2 save # ให้ start เองตอนเครื่อง boot
pm2 logs # ดู logPM2 จัดการ restart-on-crash, cluster, log rotation, zero-downtime reload ให้
ใน container/Kubernetes → ไม่ต้องใช้ PM2:
📘 คำศัพท์เร็ว ๆ (ลึกขึ้นที่ Kubernetes):
- K8s / Kubernetes = ระบบ orchestrate container — สั่งรัน/restart/scale ให้อัตโนมัติ
- replica = สำเนา container ที่รันคู่ขนาน (เช่น 3 replica = 3 instance)
- container = process ที่รันแยกใน sandbox (Docker = container ที่ใช้บ่อยสุด)
- signal = ข้อความ OS ส่งให้ process (เช่น SIGTERM = ขอให้ปิดสวย ๆ, SIGKILL = บังคับฆ่า)
- Kubernetes ทำหน้าที่ restart + scale ให้อยู่แล้ว → รัน
nodeตรง ๆ 1 process ต่อ container แล้วให้ K8s รันหลาย replica (1 process ต่อ container = หลักการ "1 process 1 container") - ใส่ PM2 ซ้อนใน container = ซ้ำซ้อน + ทำให้ K8s health check / signal ยุ่ง
ตัวอย่าง K8s manifest พร้อม probe + graceful shutdown
yaml
# deployment.yaml — ของจริง production ต้องมีครบทุกอันนี้
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 3
template:
spec:
# ⭐ ให้ K8s รอ N วินาทีให้ pod ปิดสวย ๆ ก่อนฆ่า (ค่า default 30s มักไม่พอ)
terminationGracePeriodSeconds: 45
containers:
- name: api
image: myapp:1.2.3
ports:
- containerPort: 3000
lifecycle:
preStop:
# หน่วง 10s ก่อน SIGTERM — ให้ LB ถอด pod ออกจาก endpoint ก่อน
# ไม่งั้น request ที่เข้ามาช่วงนั้นจะเจอ connection refused
exec:
command: ["sh", "-c", "sleep 10"]
# startup probe = ให้เวลา app boot (รันก่อน liveness/readiness)
startupProbe:
httpGet: { path: /healthz, port: 3000 }
failureThreshold: 30 # 30 × 2s = ให้ boot ได้ไม่เกิน 60s
periodSeconds: 2
# liveness = ตัวเองตายค้างไหม → restart
livenessProbe:
httpGet: { path: /healthz, port: 3000 }
periodSeconds: 10
# readiness = พร้อมรับ traffic ไหม → ใส่/ถอดจาก LB
readinessProbe:
httpGet: { path: /readyz, port: 3000 }
periodSeconds: 5
---
# pdb.yaml — PodDisruptionBudget: กัน K8s ฆ่า pod เยอะเกินตอน maintenance
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 2 # มีอย่างน้อย 2 pod online เสมอ
selector:
matchLabels:
app: api💡 ทำไมต้อง
preStopsleep: (Service = ตัวกลางใน K8s ที่กระจาย request ไปยัง pod ที่ยัง online อยู่ — ไม่ใช่ pod เอง)
- ตอน K8s จะปิด pod มันทำ 2 อย่างพร้อมกัน: ส่ง SIGTERM ให้ container และถอด pod ออกจาก Service (ให้ traffic ไม่วิ่งมาที่ pod นี้อีก)
- สองอย่างนี้ไม่ sync กัน ทำให้ช่วง ~5–10 วินาทีแรก load balancer (LB) อาจยังส่ง traffic มาทั้งที่ Node ปิดไปแล้ว
preStop sleepช่วยหน่วงเวลาก่อนส่ง SIGTERM จริง เพื่อให้ LB ถอด pod ออกก่อน
3. Health check + Structured logging
Health check — ให้ระบบรู้ว่าเรายังไหวไหม
Load balancer / Kubernetes ต้องรู้ว่า instance ไหน "พร้อมรับงาน" — ผ่าน health endpoint:
javascript
// liveness: process ยังไม่ตายค้างไหม (ตอบเร็ว ๆ ไม่เช็ก dependency)
app.get("/healthz", (req, res) => res.json({ status: "ok" }));
// readiness: พร้อมรับ traffic ไหม (เช็ก dependency เช่น DB ต่อได้ไหม)
let isReady = false; // ⭐ เพิ่ง boot อย่าเพิ่งบอก ready — รอ warm-up ก่อน (set true หลัง init เสร็จ)
app.get("/readyz", async (req, res) => {
if (!isReady) return res.status(503).json({ status: "warming up" });
try {
// ⭐ ใส่ timeout — ไม่งั้น DB ค้างจะทำ readiness ค้างตามไปด้วย → LB งง
await Promise.race([
pool.query("SELECT 1"), // เช็ก DB (บทที่ 9)
new Promise((_, rej) => setTimeout(() => rej(new Error("db timeout")), 2000)),
]);
res.json({ status: "ready" });
} catch {
res.status(503).json({ status: "not ready" }); // DB ล่ม → บอกว่ายังไม่พร้อม → LB ไม่ส่ง traffic มา
}
});
// ตอน boot เสร็จเปิด ready
app.listen(3000, () => { isReady = true; logger.info("ready"); });💡 แยก liveness (ตัวเองตายไหม → ถ้าตาย restart) จาก readiness (พร้อมรับงานไหม → ถ้าไม่ ตัดออกจาก pool ชั่วคราว) — รายละเอียดที่ Kubernetes บทที่ 7
Structured logging ด้วย pino
console.log ใช้ debug ตอน dev ได้ แต่ production ต้อง log เป็น JSON (มี level, timestamp, context) เพื่อให้ระบบเก็บ log ค้นหา/กรองได้ — pino เป็น default ที่แนะนำสำหรับ Node ปี 2026 (เร็วที่สุด, JSON ตั้งแต่ default, มี ecosystem ครบ)
📘 คำศัพท์: Loki / Elasticsearch = ระบบเก็บ + ค้นหา log แบบกลาง (ส่ง log จากทุก service ไปกองรวมกัน แล้วค้นได้เหมือน Google) — รายละเอียดที่ Observability
bash
npm install pino pino-http # pino-http = middleware log request/response ให้ Expressjavascript
import pino from "pino";
import pinoHttp from "pino-http";
const logger = pino({
level: process.env.LOG_LEVEL ?? "info",
// serializers = แปลง object ก้อนใหญ่ให้เป็นรูปอ่านง่าย+ปลอดภัย (ไม่ leak field ที่ไม่ควร log)
serializers: {
err: pino.stdSerializers.err,
req: pino.stdSerializers.req,
res: pino.stdSerializers.res,
},
// dev: อ่านง่ายมีสี (pino-pretty) · production: JSON ดิบ ๆ
});
// ใส่ middleware → ทุก request จะมี log อัตโนมัติ (พร้อม request id, latency)
app.use(pinoHttp({ logger }));
logger.info({ userId: 42, action: "login" }, "user เข้าสู่ระบบ");
// production output (JSON): {"level":30,"time":...,"userId":42,"action":"login","msg":"user เข้าสู่ระบบ"}
logger.error({ err }, "query พัง"); // log error พร้อม context (stack trace ครบจาก serializer)ทำไม JSON: ระบบ log จะ index field (userId, action) ให้ค้นได้ — เช่น "หา log ทั้งหมดของ userId 42 ที่ level error" · ส่วน console.log ได้ text ก้อนเดียว ค้นยาก
💡 อย่า log ความลับ (password, token, เลขบัตร) ลง log เด็ดขาด — log รั่วได้ · pino มี
redactตัด field พวกนี้อัตโนมัติ
Observability ครบสามขา: log + metrics + traces ด้วย OpenTelemetry
ปี 2026 มาตรฐานอุตสาหกรรมคือ OpenTelemetry (OTel) — spec กลางที่ vendor-neutral สำหรับเก็บ log/metrics/traces ส่งไปยัง backend อะไรก็ได้ (Datadog, New Relic, Grafana, Jaeger, ...)
bash
npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node \
@opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-httpjavascript
// otel.mjs — โหลดก่อนโค้ดอื่นทุกอย่าง (node --import ./otel.mjs server.mjs)
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
const sdk = new NodeSDK({
serviceName: "my-api",
traceExporter: new OTLPTraceExporter({ url: process.env.OTEL_ENDPOINT }),
instrumentations: [getNodeAutoInstrumentations()], // auto-trace Express/HTTP/pg/Redis ให้
});
sdk.start();bash
# รันแอปพร้อม OTel
node --import ./otel.mjs server.mjsได้อะไร: distributed trace (ตามคำขอข้าม service ได้ว่ามันใช้เวลาที่ไหนนาน), latency metrics, error rate — ครบ ไม่ต้องเขียนเอง · ลึกขึ้นที่ Observability
4. Security checklist — อย่าให้โดนแฮก
backend ที่เปิดสู่อินเทอร์เน็ตคือเป้า — เช็กลิสต์ขั้นต่ำ (ลึกขึ้นที่ Security + API Design บทที่ 4):
bash
npm install helmet express-rate-limit rate-limit-redis ioredis argon2
# helmet v8+ รองรับ ESM ในตัว (เวอร์ชันเก่ากว่านี้เป็น CJS-only)javascript
import helmet from "helmet"; // ตั้ง security headers ให้อัตโนมัติ
import rateLimit from "express-rate-limit";
import { RedisStore } from "rate-limit-redis";
import Redis from "ioredis";
app.use(helmet()); // ป้องกัน header-based attack หลายชนิด
app.use(express.json({ limit: "100kb" })); // จำกัดขนาด body กัน payload ระเบิด memory
// ⭐ ⚠️ สำคัญมาก: express-rate-limit ค่า default เก็บ counter ใน RAM ของ process เดียว
// → ถ้ารันหลาย instance หลัง LB จะ count แยกกัน → ทะลุ limit จริงเป็นเท่าตัว instance
// → production ต้องใช้ external store (Redis) ที่แชร์ระหว่าง instance
const redis = new Redis(process.env.REDIS_URL);
app.use(rateLimit({
windowMs: 60_000,
max: 100, // 100 req/นาที/IP — รวมกันทุก instance
store: new RedisStore({ sendCommand: (...args) => redis.call(...args) }),
}));🚀 โซนขั้นสูง — ข้ามได้ถ้ายังใหม่กับ security: CRIME/BREACH กับ
compression()
- CRIME/BREACH คือชื่อการโจมตีแบบ "side-channel" (เดาความลับทางอ้อมจากพฤติกรรมของระบบ แทนที่จะแฮกตรง ๆ) — ในที่นี้คือเดา session token/CSRF token จากขนาดของ response ที่ถูกบีบอัด (gzip)
- เกิดตอนไหน: ถ้าใช้
compression()middleware (gzip response อัตโนมัติ) กับ endpoint ที่มีทั้ง "ความลับ" (เช่น CSRF token, session token) และ "input จาก user" ปนอยู่ในหน้าตอบกลับเดียวกัน (เช่น endpoint ที่ echo token กลับมาในหน้า) — ผู้โจมตีลองยิง input ต่าง ๆ แล้วดูว่าขนาด response บีบอัดแล้วเล็กลงไหม จะไล่เดาตัวอักษรของ token ได้ทีละตัว- ทางแก้ (เลือกอย่างใดอย่างหนึ่ง):
- ปิด compression สำหรับ auth/CSRF endpoint
- ใช้
Cache-Control: no-transform+ เปลี่ยน secret บ่อย ๆ- เช็คให้แน่ใจว่า response นั้นไม่มีทั้ง secret + user input ปนกันในหน้าเดียว
🔐 Password hashing: เก็บ password ต้อง hash ด้วย Argon2id (ตัวเลือกอันดับ 1 ตาม OWASP ปี 2026 เพราะเป็น memory-hard — ต้าน GPU/ASIC crack ได้ดีกว่า) —
npm install argon2แล้วawait argon2.hash(pwd, { type: argon2.argon2id })· bcrypt ยังปลอดภัยและใช้ได้อยู่ ถ้าโปรเจกต์เก่ามีอยู่แล้วไม่จำเป็นต้องรีบ migrate ด่วน · แต่อย่าใช้ SHA/MD5 เด็ดขาด (เร็วเกินไป crack ง่าย — คนละระดับกับ bcrypt)
| เรื่อง | ทำอะไร | บทที่เกี่ยว |
|---|---|---|
| SQL injection | parameterized query ($1) เสมอ | บทที่ 9 |
| Command injection | execFile/spawn ไม่ใช่ exec กับ user input | บทที่ 10 |
| Input validation | validate ทุก input ด้วย Zod | บทที่ 7, 9 |
| Secrets | env เท่านั้น, ไม่ commit .env, ไม่ log | บทที่ 7 |
| Dependencies | npm audit หา vuln, อัปเดต lockfile สม่ำเสมอ | บทที่ 1 |
| HTTP headers | helmet | นี้ |
| Rate limiting | express-rate-limit + Redis store (หลาย instance) | นี้ |
| Password hashing | Argon2id (argon2) แนะนำสุด · bcrypt ยังใช้ได้ · ห้าม SHA/MD5 | นี้ |
| CRIME/BREACH | ระวัง compression() กับ response มีทั้ง secret + user input | นี้ |
| ไม่รันเป็น root | USER node ใน Docker | ข้อ 1 |
bash
npm audit # สแกน dependency หาช่องโหว่ที่รู้แล้ว
npm audit fix # แก้ที่แก้ได้อัตโนมัติ⚠️ Pitfall:
npm installpackage มั่ว ๆ จาก npm โดยไม่ตรวจ — npm มี supply chain attack (package ปลอม/ถูกแฮก ฝัง malware) ตรวจชื่อให้ดี ระวัง typo — มี typosquatting (ตั้งชื่อ package สะกดคล้ายของดัง เช่นexpresแทนexpressหวังคนพิมพ์ผิดแล้ว install) ดู Security บทที่ 6
5. Modern Node — ฟีเจอร์ที่ควรรู้ (2026)
Node สมัยใหม่ (Node 22 LTS เป็น default ของ production ปี 2026 · Node 20 LTS ยัง maintenance อยู่ · Node 24+ เป็น current) มีของในตัวที่เมื่อก่อนต้องลง package — ลด dependency ลงเยอะ:
javascript
// fetch built-in (v18+) — ไม่ต้องลง axios/node-fetch (บทที่ 5)
const res = await fetch(url);
// node:test + assert built-in (v20+) — ไม่ต้องลง Jest สำหรับงานพื้นฐาน (บทที่ 8)
// --watch (v18+) — restart อัตโนมัติ ไม่ต้องลง nodemon
// node --watch server.mjs
// --env-file (v20.6+) — โหลด .env ไม่ต้องลง dotenv (บทที่ 7)
// node --env-file=.env server.mjs
// glob built-in (v22+, stable ใน 22.x) — หาไฟล์ตาม pattern
// (ในเวอร์ชันก่อนหน้าเป็น experimental — เช็ก docs ของรุ่นที่ใช้)
import { glob } from "node:fs/promises";
for await (const file of glob("src/**/*.mjs")) console.log(file);
// Permission Model (ยังเป็น experimental ใน Node 22) — จำกัดสิทธิ์โปรแกรม
// Node 22+ ใช้ flag --permission (ของเก่า --experimental-permission ยังใช้ได้แต่ deprecated)
// node --permission --allow-fs-read=./data server.mjs
// → โค้ดเขียนไฟล์/เปิด network ที่ไม่ได้อนุญาตจะ throw ทันทีมีแม้แต่ single executable application (SEA) — รวม Node runtime + โค้ดของเราเป็นไฟล์ .exe (Windows) / binary (Linux/macOS) ไฟล์เดียว แจกให้ user ที่ไม่มี Node ก็รันได้ทันที (ยังเป็น experimental — เหมาะกับ CLI tool ไม่ใช่ web server)
TypeScript กับ Node
โปรเจกต์จริงส่วนใหญ่เขียน Node ด้วย TypeScript (TypeScript track) — มี 3 ทางเลือก:
bash
# 1. tsx — รัน .ts ตรง ๆ ระหว่าง dev (เร็ว นิยมสุด)
npx tsx watch src/server.ts
# 2. native type stripping — Node รัน .ts ได้เองโดยลบ type ทิ้ง (ไม่ check type)
# Node 22.6+: ต้องใส่ flag --experimental-strip-types
# เวอร์ชันใหม่กว่านี้อาจเปิด default แล้ว — เช็ก release notes ของ Node ที่ใช้จริงก่อน
node --experimental-strip-types src/server.ts
node src/server.ts # ถ้า Node เวอร์ชันที่ใช้เปิด strip-types เป็น default แล้ว ไม่ต้องใส่ flag
# 3. build เป็น .js ก่อน deploy (production)
npx tsc # แปลง src/*.ts → dist/*.js แล้วรัน node dist/server.js⚠️ ข้อจำกัดของ strip-types: Node ลบ type ทิ้งเฉย ๆ ไม่ได้ "แปลง" ดังนั้นฟีเจอร์ TypeScript ที่ต้องการ runtime transform (โค้ดที่ต้องถูกแปลงเป็นโค้ดจริงที่รันได้ ไม่ใช่แค่ลบ type annotation ทิ้ง) จะ ใช้ไม่ได้ —
enum,namespace, decorator พร้อม metadata (ที่ NestJS ใช้) · ถ้าใช้ NestJS ตอนนี้ยังต้อง build ด้วยtsc/swcอยู่ · และ strip-types ไม่ check type — ต้องรันtsc --noEmitแยกเพื่อ type-check
💡 ข้อมูล ณ 2026: Node กำลังเดินหน้าให้รัน TypeScript ได้ในตัว (strip type) — แต่ production ตอนนี้ยังนิยม build ด้วย
tsc/esbuild/swcก่อน deploy เพื่อความเสถียร · dev ใช้tsx
Deno และ Bun — runtime ทางเลือก
🚀 โซนขั้นสูง — ข้ามได้ ส่วนนี้แค่ให้ "รู้จักไว้" ว่ามี runtime อื่นนอกจาก Node — มือใหม่โฟกัส Node ให้แน่นก่อน ไม่ต้องรีบลอง Deno/Bun
Node ไม่ใช่ runtime เดียวของ JS แล้ว — รู้จักไว้:
| Runtime | จุดเด่น | สถานะ (2026) |
|---|---|---|
| Node.js | ecosystem ใหญ่สุด, เสถียร, มาตรฐานอุตสาหกรรม | ครองตลาด ใช้ใน production ทั่วโลก |
| Deno | TypeScript ในตัว, security-first (permission), std lib ดี | นิยมในงานใหม่/script, เข้ากับ npm ได้แล้ว |
| Bun | เร็วมาก, มี bundler/test/package manager ในตัว | โตเร็ว, ใช้ใน production มากขึ้น |
💡 เริ่มต้น/หางาน → Node (ความต้องการสูงสุด, สิ่งที่เรียนมาทั้งเล่มใช้ได้) · Bun/Deno น่าจับตาและลองได้ แต่ Node คือพื้นฐานที่ทุกอย่างอ้างอิง — เข้าใจ Node แล้วย้ายไป Bun/Deno ง่าย เพราะ API ส่วนใหญ่เลียนแบบ Node
6. 🚀 Roadmap — ก้าวต่อไป
คุณเข้าใจ Node ตั้งแต่ runtime ถึง production แล้ว ต่อไป:
- ➡️ NestJS — เป้าหมายหลักของเล่มนี้ ตอนนี้คุณพร้อมแล้ว: รู้ว่า decorator/DI/middleware/pipeline ที่ Nest ใช้ — ข้างใต้คือ
http+ Express + EventEmitter + การจัดการ async/error ที่คุณเพิ่งเรียน · ประโยค "ปัญหาของ Express เปล่า ๆ" ใน Nest บทที่ 0 จะชัดเจนทันที - React + Next.js — fullstack: Next.js รัน React บน Node (SSR/Server Components/Server Actions) — ความรู้ Node ทำให้เข้าใจว่าฝั่ง server ทำงานยังไง
- API Design — ออกแบบ REST/GraphQL/gRPC ให้ดี (ต่อยอด REST API บทที่ 9)
- Database — SQL ลึก, indexing, transaction (ต่อยอด
pgบทที่ 9) - Microservices + Observability — เมื่อระบบโตเป็นหลายบริการ
แหล่งอ้างอิงทางการ
- Node.js Docs — เอกสาร API ทางการ (อ่านได้จริง ครบ)
- Node.js Learn — บทเรียนทางการ
- Express · pino · Zod
- 12 Factor App — หลักการทำแอปที่ deploy ดี
🛠️ Checkpoint 11 — ปิดเล่ม
- เขียน
Dockerfilemulti-stage สำหรับ API บทที่ 9 (slim, non-root,--omit=dev,.dockerignore) แล้วdocker build+docker runให้ใช้งานได้ - เพิ่ม
/healthz(liveness) +/readyz(เช็ก DB จริง) แล้วลองหยุด DB ดูว่า/readyzตอบ 503 - เปลี่ยน logging ทั้งแอปจาก
console.logเป็นpino(มี level + context) — ดู output ที่เป็น JSON - รัน
npm auditกับโปรเจกต์ตัวเอง — มีช่องโหว่ไหม ลองอ่านว่าแต่ละอันคืออะไร - เพิ่ม
helmet+express-rate-limit+ จำกัดขนาด body แล้วยิง request เกิน limit ดูว่าโดนบล็อก - ลองรัน TypeScript ด้วย
tsxและnode --experimental-strip-typesเทียบกัน - ปิดเล่ม: เปิด NestJS บทที่ 0 อ่านอีกครั้ง — สังเกตว่าตอนนี้เข้าใจทุกประโยคแล้ว แล้วเริ่ม track NestJS ได้เลย 🎉
สรุปบทที่ 11 + ปิดเล่ม
- Docker: multi-stage +
node:22-slim+USER node+npm ci --omit=dev+.dockerignore+HEALTHCHECK+CMD ["node", ...](ไม่ใช่ npm) - Process manager: PM2 บน VM (
-i max) · ใน container/K8s รันnodeตรง ๆ +terminationGracePeriodSeconds+preStop sleep+ startup/liveness/readiness probe +PodDisruptionBudget - Health check: liveness (
/healthz) แยกจาก readiness (/readyzเช็ก DB มี timeout + warm-up gate) · structured logging ด้วย pino + pino-http (JSON) · OpenTelemetry สำหรับ trace/metrics - Security: parameterized query, validate input,
helmetv8+,express-rate-limit+ Redis store (สำคัญสำหรับหลาย instance), Argon2id สำหรับ password, ระวัง CRIME/BREACH กับcompression(), จำกัด body, secrets ใน env,npm audit, ไม่รัน root - Modern Node:
fetch/node:test/--watch/--env-file/globbuilt-in ·--permission(experimental sandbox) · TypeScript ผ่านtsx/strip-types (มีข้อจำกัด)/tsc· Deno/Bun = ทางเลือก แต่ Node คือพื้นฐาน - ก้าวต่อไป → NestJS (คุณพร้อมแล้ว) · Next.js · API Design · Database · Microservices
🎉 จบ track Node.js — คุณเดินทางจาก "JavaScript คือภาษาในเบราว์เซอร์" มาถึง "backend ที่ deploy ขึ้น production ได้จริง" และที่สำคัญที่สุด: คุณ เข้าใจว่าใต้ framework มีอะไร — ซึ่งคือสิ่งที่ทำให้คุณใช้ NestJS/Next.js แบบรู้จริง ไม่ใช่แบบงม ๆ