1. Cloudflare Workers Deployment — wrangler deploy
Cloudflare Workers คือ Serverless Runtime ที่รันโค้ด JavaScript ใกล้กับผู้ใช้มากที่สุด ผ่าน Edge Network ของ Cloudflare ซึ่งมีมากกว่า 300 จุดทั่วโลก ข้อดีเมื่อเทียบกับ Server แบบเดิมคือ ไม่มี Cold Start แบบ Traditional Serverless, Latency ต่ำมาก, และ Scale อัตโนมัติโดยไม่ต้องตั้งค่า
เครื่องมือหลักในการ Deploy คือ Wrangler CLI ซึ่งเป็น Command-line Tool ของ Cloudflare ใช้สำหรับ Develop, Test, และ Deploy Workers พร้อมทั้งจัดการ Secrets และ KV Storage
- wrangler.toml — ไฟล์ Config หลักของโปรเจกต์ Workers ระบุ name, main entry, compatibility_date และ bindings ต่างๆ เช่น Environment Variables หรือ KV Namespace
- npx wrangler dev — รัน Workers ในเครื่องแบบ Local Development มี Hot Reload และ Simulate Edge Environment ได้ใกล้เคียงกับ Production มากที่สุด
- npx wrangler deploy — Deploy โค้ดขึ้น Cloudflare Network จริง ใช้เวลาไม่กี่วินาที และ Rollback ทันทีหากมีปัญหา
โครงสร้าง Deploy Pipeline — Cloudflare Workers
─────────────────────────────────────────────────
Local Dev Cloudflare Edge
───────────────── ─────────────────────────
wrangler.toml workers.dev Domain
src/index.js ──────► bookeasy-api.your-name
.dev.vars .workers.dev
(local secrets)
Cloudflare Pages
bookeasy/ ─────────────────────────
dist/ ──────► bookeasy.pages.dev
(Vite build)
─────────────────────────────────────────────────
หลัง Deploy แล้ว ใช้ npx wrangler tail เพื่อดู Real-time Logs ของ Workers
คล้ายกับ console.log ใน Development แต่ทำงานบน Production
ช่วย Debug ได้เมื่อ API ตอบสนองผิดปกติหลัง Deploy
2. Environment Variables บน Production — Wrangler Secrets
ใน Development เราเก็บ Credentials เช่น Supabase URL หรือ API Key ไว้ในไฟล์ .dev.vars
ซึ่งไม่ควร Commit ขึ้น Git แต่บน Production เราต้องใช้ Wrangler Secrets
ซึ่งเก็บค่า Encrypted ใน Cloudflare Dashboard และ Inject ให้ Workers ณ Runtime โดยอัตโนมัติ
ข้อแตกต่างระหว่าง Secrets กับ Environment Variables ปกติ:
- Secrets — เข้ารหัสไว้ใน Cloudflare, ไม่แสดงใน Dashboard หลังตั้งค่า, เหมาะสำหรับ Database URL, API Keys, JWT Secret ที่ไม่ควรเปิดเผย
-
Vars (wrangler.toml) — ตั้งค่าได้ใน
wrangler.tomlภายใต้[vars]เหมาะสำหรับค่าที่ไม่ Sensitive เช่น APP_ENV, MAX_UPLOAD_SIZE
วิธีเข้าถึง Secrets ใน Hono API
─────────────────────────────────────────────────
// ใน Cloudflare Workers (Hono)
app.get('/api/services', async (c) => {
const supabaseUrl = c.env.SUPABASE_URL; ← จาก wrangler secret
const supabaseKey = c.env.SUPABASE_KEY; ← จาก wrangler secret
const client = createClient(supabaseUrl, supabaseKey);
const { data } = await client.from('services').select('*');
return c.json({ data });
});
─────────────────────────────────────────────────3. Vite Build + Deploy Frontend — Cloudflare Pages
Cloudflare Pages คือบริการ Static Site Hosting ของ Cloudflare ที่เชื่อมต่อกับ Git Repository ได้โดยตรง หรือ Deploy ผ่าน Wrangler CLI ก็ได้ เหมาะสำหรับ Vite + React App ที่ Build เป็น Static Files
ขั้นตอนหลักในการ Deploy Frontend:
-
สร้าง .env.production — ไฟล์นี้ Vite จะโหลดอัตโนมัติเมื่อรัน
npm run buildใส่VITE_API_URLชี้ไปยัง Workers URL จริง (ไม่ใช่ localhost) -
npm run build — Vite สร้างโฟลเดอร์
dist/ที่มี Optimized HTML, CSS, JS พร้อม Tree Shaking และ Code Splitting อัตโนมัติ -
npx wrangler pages deploy dist — อัปโหลด
dist/ขึ้น Cloudflare Pages และได้รับ URL จริงทันที
เมื่อ Frontend อยู่ที่ bookeasy.pages.dev และ Backend อยู่ที่ bookeasy-api.workers.dev
ต้องตั้งค่า CORS ใน Hono ให้ยอมรับ Origin จาก Pages URL จริง
ไม่ใช่ * หรือ localhost อีกต่อไป
4. Testing เบื้องต้นใน JavaScript — Vitest
Vitest คือ Testing Framework สำหรับ JavaScript/TypeScript ที่ออกแบบมาให้ทำงานร่วมกับ Vite ได้อย่างสมบูรณ์ ใช้ Syntax เดียวกับ Jest แต่เร็วกว่าเพราะใช้ ESM Native และไม่ต้องตั้งค่า Babel หรือ Transform พิเศษ
ประเภทของ Test ที่ควรรู้จัก:
-
Unit Test — ทดสอบฟังก์ชันเดียวแบบ Isolated ไม่มี Network, Database, หรือ UI
เหมาะสำหรับ Utility Functions เช่น
formatPrice(),formatDate() - Integration Test — ทดสอบการทำงานร่วมกันของหลาย Module เช่น ทดสอบว่า API Route ส่งข้อมูลถูกต้องเมื่อรับ Request จริง
- E2E Test (End-to-End) — ทดสอบแบบ Full Flow ตั้งแต่ผู้ใช้เปิดเบราว์เซอร์ จนถึงข้อมูลบันทึกลง Database ใช้เครื่องมืออย่าง Playwright หรือ Cypress
โครงสร้าง Test File — Vitest
─────────────────────────────────────────────────
describe('formatPrice', () => { ← Test Suite
it('formats Thai baht', () => { ← Test Case
expect(formatPrice(150)) ← Assertion
.toBe('฿150.00');
});
it('handles zero', () => {
expect(formatPrice(0)).toBe('฿0.00');
});
});
ผลลัพธ์เมื่อ npm test:
─────────────────────────────────────────────────
✓ formatPrice > formats Thai baht
✓ formatPrice > handles zero
✓ formatPrice > handles large numbers
Test Files: 1 passed (1)
Tests: 3 passed (3)
─────────────────────────────────────────────────
Utility Functions เช่น formatPrice(), formatDate(), validateEmail()
คือจุดเริ่มต้น Testing ที่ดีที่สุด เพราะไม่มี Side Effect และทดสอบง่าย
เมื่อ Confidence สูงขึ้นค่อย Test ส่วนที่ซับซ้อนขึ้นอย่าง API Handlers
5. สรุปรายวิชาและ Next Steps
ตลอด 15 สัปดาห์ที่ผ่านมา เราสร้าง BookEasy — Web Application จองบริการ ตั้งแต่ศูนย์จนถึง Production โดยใช้ Stack สมัยใหม่ที่ใช้จริงในอุตสาหกรรม:
BookEasy Stack — สรุป 15 สัปดาห์ ───────────────────────────────────────────────── W1 — แนะนำรายวิชา: Client-Server, HTTP, Tools W2 — HTML/CSS/JS: โครงสร้างโปรเจกต์ BookEasy W3 — Vite + React: JSX, Components, Props W4 — TanStack Router: File-based Routing W5 — Hono + Cloudflare Workers: REST API W6 — Supabase Database: PostgreSQL + RLS W7 — CRUD สมบูรณ์: GET/POST/PATCH/DELETE W8 — TanStack Query: Data Fetching + Cache W9 — Supabase Auth: Login, Register, Session W10 — Supabase Storage: อัปโหลดรูปบริการ W11 — Shadcn/ui + Tailwind: UI/UX ที่สวยงาม W12 — Custom Hooks: useServices, useBookings W13 — โครงงานทีม: วิเคราะห์ ออกแบบ พัฒนา W14 — นำเสนอ: Demo, Documentation W15 — Deploy + Testing + สรุปรายวิชา ← คุณอยู่ที่นี่ ─────────────────────────────────────────────────
ทักษะที่ได้จากรายวิชานี้ตรงกับสิ่งที่อุตสาหกรรม IT ต้องการ:
- Frontend Developer — React, Vite, TanStack Router/Query, Tailwind, Shadcn/ui
- Backend Developer — Hono, Cloudflare Workers, REST API, Supabase
- Full Stack Developer — ทำได้ทั้งสองฝั่ง ครอบคลุมทุกชั้นของแอปพลิเคชัน
- DevOps / Deployment — Wrangler CLI, Cloudflare Pages, Environment Management
Next Steps ที่แนะนำ สำหรับการพัฒนาทักษะต่อจากรายวิชานี้:
- TypeScript — เพิ่ม Type Safety ให้ JavaScript Codebase เดิม Hono และ Vite รองรับ TypeScript ได้ทันทีโดยแทบไม่ต้องเปลี่ยนโครงสร้าง
- Testing (Vitest + Playwright) — เขียน Unit Test ให้ครอบคลุม Utility Functions และ E2E Test สำหรับ User Flows สำคัญ
- CI/CD (GitHub Actions) — ตั้งค่า Pipeline ให้ Run Tests อัตโนมัติทุก Push และ Deploy อัตโนมัติเมื่อ Tests ผ่านทั้งหมด
- Drizzle ORM หรือ Prisma — ใช้ Type-safe ORM แทนการเขียน SQL โดยตรง ทำให้ Query ปลอดภัยขึ้นและ Migrate Schema ได้ง่ายขึ้น
docs.hono.dev — Official Hono Documentation ครบถ้วน | vitest.dev — Vitest Guide และ API Reference | developers.cloudflare.com — Workers และ Pages Documentation | supabase.com/docs — Supabase Reference ทุก Feature
# snippet 1: Deploy ครบวงจร
# --- Deploy Backend (Hono API) ---
cd bookeasy-api
# ตั้งค่า secrets บน production
npx wrangler secret put SUPABASE_URL
npx wrangler secret put SUPABASE_KEY
# Deploy
npx wrangler deploy
# ✅ Deployed to: https://bookeasy-api.your-name.workers.dev
# --- Deploy Frontend (Vite → Cloudflare Pages) ---
cd bookeasy
# แก้ .env.production
echo "VITE_API_URL=https://bookeasy-api.your-name.workers.dev" > .env.production
npm run build
# dist/ folder พร้อม deploy
npx wrangler pages deploy dist --project-name bookeasy
# ✅ Deployed to: https://bookeasy.pages.dev
// snippet 2: Vitest — unit test เบื้องต้น
// src/utils/formatPrice.js
export function formatPrice(price) {
return new Intl.NumberFormat('th-TH', {
style: 'currency',
currency: 'THB',
}).format(price);
}
// src/utils/formatPrice.test.js
import { describe, it, expect } from 'vitest';
import { formatPrice } from './formatPrice';
describe('formatPrice', () => {
it('formats Thai baht correctly', () => {
expect(formatPrice(150)).toBe('฿150.00');
});
it('handles zero', () => {
expect(formatPrice(0)).toBe('฿0.00');
});
it('handles large numbers', () => {
expect(formatPrice(1500)).toBe('฿1,500.00');
});
});
# snippet 3: รัน tests และ build check
# ติดตั้ง Vitest
npm install -D vitest
# เพิ่มใน package.json
# "test": "vitest run"
# รัน tests
npm test
# ✅ 3 tests passed
# ตรวจ build ก่อน deploy จริง
npm run build
# ✅ dist/index.html xx kB
# ✅ dist/assets/index-xxx.js xx kB
# Preview build ในเครื่อง
npm run preview
# เปิด http://localhost:4173
🧪 ปฏิบัติการ Lab 15 — BookEasy LIVE: Deploy จริง
นำ BookEasy ที่สร้างมาตลอด 14 สัปดาห์ Deploy ขึ้น Cloudflare จริง และเขียน Test ชุดแรก
ต่อยอดจากทุก Lab — นำ BookEasy ที่สร้างมาทั้งหมด Deploy ขึ้น Cloudflare จริง
ก่อน Deploy ต้องปรับ Config ทั้ง Backend และ Frontend ให้พร้อมสำหรับ Production:
-
แก้ไข CORS ใน Hono API (
bookeasy-api/src/index.js):- เปลี่ยน
origin: 'http://localhost:5173'เป็น URL จริงของ Cloudflare Pages เช่น'https://bookeasy.pages.dev' - ถ้ายังไม่รู้ URL Pages ให้ใช้
origin: '*'ชั่วคราวก่อน แล้วเปลี่ยนหลัง Deploy Frontend
- เปลี่ยน
-
สร้างไฟล์
bookeasy/.env.production:- เพิ่มบรรทัด
VITE_API_URL=https://bookeasy-api.your-name.workers.dev - แทนที่
your-nameด้วยชื่อจริงที่จะใช้ใน Cloudflare - ตรวจสอบว่า
.env.productionอยู่ใน.gitignore
- เพิ่มบรรทัด
ไฟล์ bookeasy-api/src/index.js มี CORS ที่ชี้ไป Pages URL จริง
และไฟล์ bookeasy/.env.production มี VITE_API_URL ที่ถูกต้อง
ใช้ app.use('*', cors({ origin: 'https://bookeasy.pages.dev' })) ใน Hono
ตรวจสอบ URL ให้ถูกต้องทั้ง Protocol (https) และ Domain ก่อน Deploy
Deploy Hono API ขึ้น Cloudflare Workers จริงตามขั้นตอนต่อไปนี้:
-
ตั้งค่า Production Secrets ด้วย Wrangler:
- รัน
npx wrangler secret put SUPABASE_URL— กรอก URL จาก Supabase Dashboard - รัน
npx wrangler secret put SUPABASE_KEY— กรอกanonkey
- รัน
-
Deploy:
- รัน
npx wrangler deployจากโฟลเดอร์bookeasy-api - บันทึก URL ที่ได้รับ เช่น
https://bookeasy-api.your-name.workers.dev
- รัน
-
ทดสอบ API จริงด้วย curl:
curl https://bookeasy-api.your-name.workers.dev/api/services- ต้องได้รับ JSON Response ที่มี
dataarray จาก Supabase
รัน curl ไปยัง Workers URL จริงแล้วได้ข้อมูลบริการ (Services) จาก Supabase
Status Code เป็น 200 และ Body เป็น JSON ที่ถูกต้อง
ถ้า curl ได้ Error 500 ให้รัน npx wrangler tail เพื่อดู Real-time Logs
มักเกิดจาก Secrets ที่ตั้งค่าผิด หรือ Supabase URL ที่พิมพ์ผิด
Deploy Vite + React Frontend ขึ้น Cloudflare Pages:
-
Build Frontend:
- รัน
npm run buildจากโฟลเดอร์bookeasy - ตรวจสอบว่าไม่มี Error ใดๆ และโฟลเดอร์
dist/ถูกสร้างขึ้น - Vite จะอ่าน
.env.productionอัตโนมัติ ทำให้ API URL ในโค้ดชี้ไป Workers จริง
- รัน
-
Deploy บน Cloudflare Pages:
- รัน
npx wrangler pages deploy dist --project-name bookeasy - บันทึก URL ที่ได้รับ เช่น
https://bookeasy.pages.dev
- รัน
-
ทดสอบในเบราว์เซอร์:
- เปิด URL จริงในเบราว์เซอร์ ทดสอบ Login ด้วย Supabase Auth
- ตรวจสอบว่าหน้าแสดงบริการโหลดได้จาก Workers API จริง
- ทดสอบการจองบริการให้ครบ Flow
เปิด https://bookeasy.pages.dev ในเบราว์เซอร์ และสามารถ Login, ดูบริการ, และจองบริการ
ได้ครบทุก Feature โดยข้อมูลมาจาก Supabase จริงผ่าน Cloudflare Workers
ถ้า Frontend โหลดได้แต่ข้อมูลไม่ขึ้น ให้เปิด Browser DevTools → Network
ตรวจสอบว่า Request ไปยัง Workers URL ถูกต้อง และ CORS Header ในใน Response มี
Access-Control-Allow-Origin ชี้ไปยัง Pages Domain
เพิ่ม Testing ให้กับโปรเจกต์ BookEasy โดยเริ่มจาก Utility Function:
-
ติดตั้ง Vitest:
- รัน
npm install -D vitestในโฟลเดอร์bookeasy - เพิ่ม
"test": "vitest run"ในpackage.jsonภายใต้scripts
- รัน
-
สร้าง Utility Function และ Test File:
- สร้าง
src/utils/formatPrice.jsตาม Snippet 2 ด้านบน - สร้าง
src/utils/formatPrice.test.jsพร้อม Test Cases อย่างน้อย 3 กรณี
- สร้าง
-
รัน Tests:
- รัน
npm test— ต้องผ่านทั้ง 3 Test Cases - ลอง Edit ค่า Expected ให้ผิด เพื่อดูว่า Vitest แสดง Error อย่างไร
- แก้กลับให้ถูกต้องและ Confirm ว่า Tests ผ่านทั้งหมด
- รัน
รัน npm test แล้วเห็นผลลัพธ์ 3 tests passed
ไม่มี Test ที่ Fail และ Terminal แสดง Test Files: 1 passed
ถ้า Intl.NumberFormat ให้ผลลัพธ์ต่างจากที่คาด ให้ตรวจสอบว่า Node.js Version
ที่ใช้รองรับ th-TH Locale โดยทดสอบใน Node.js Console ก่อน:
new Intl.NumberFormat('th-TH', { style: 'currency', currency: 'THB' }).format(150)