สัปดาห์ที่ 15

Deploy บน Cloudflare Workers, Testing & สรุปรายวิชา

CLO3 CLO7
📖 ทฤษฎี

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)
  ─────────────────────────────────────────────────
เคล็ดลับ — ตรวจ Logs หลัง Deploy

หลัง 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 จริงทันที
หมายเหตุ — CORS บน Production

เมื่อ 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)
  ─────────────────────────────────────────────────
เคล็ดลับ — เริ่มต้น Test จาก Utility Functions

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

💻 โค้ดตัวอย่าง
deploy.sh — Deploy BookEasy ขึ้น Cloudflare Bash
# 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
src/utils/formatPrice.test.js — Unit Test ด้วย Vitest JavaScript
// 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');
  });
});
terminal — รัน Tests และ Build Check Bash
# 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 11

ต่อยอดจากทุก Lab — นำ BookEasy ที่สร้างมาทั้งหมด Deploy ขึ้น Cloudflare จริง

1
เตรียม Production Environment (CORS + .env.production)

ก่อน Deploy ต้องปรับ Config ทั้ง Backend และ Frontend ให้พร้อมสำหรับ Production:

  1. แก้ไข CORS ใน Hono API (bookeasy-api/src/index.js):
    • เปลี่ยน origin: 'http://localhost:5173' เป็น URL จริงของ Cloudflare Pages เช่น 'https://bookeasy.pages.dev'
    • ถ้ายังไม่รู้ URL Pages ให้ใช้ origin: '*' ชั่วคราวก่อน แล้วเปลี่ยนหลัง Deploy Frontend
  2. สร้างไฟล์ 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

2
Deploy Backend (wrangler secret + wrangler deploy + curl test)

Deploy Hono API ขึ้น Cloudflare Workers จริงตามขั้นตอนต่อไปนี้:

  1. ตั้งค่า Production Secrets ด้วย Wrangler:
    • รัน npx wrangler secret put SUPABASE_URL — กรอก URL จาก Supabase Dashboard
    • รัน npx wrangler secret put SUPABASE_KEY — กรอก anon key
  2. Deploy:
    • รัน npx wrangler deploy จากโฟลเดอร์ bookeasy-api
    • บันทึก URL ที่ได้รับ เช่น https://bookeasy-api.your-name.workers.dev
  3. ทดสอบ API จริงด้วย curl:
    • curl https://bookeasy-api.your-name.workers.dev/api/services
    • ต้องได้รับ JSON Response ที่มี data array จาก Supabase
เกณฑ์การผ่าน

รัน curl ไปยัง Workers URL จริงแล้วได้ข้อมูลบริการ (Services) จาก Supabase Status Code เป็น 200 และ Body เป็น JSON ที่ถูกต้อง

คำแนะนำ

ถ้า curl ได้ Error 500 ให้รัน npx wrangler tail เพื่อดู Real-time Logs มักเกิดจาก Secrets ที่ตั้งค่าผิด หรือ Supabase URL ที่พิมพ์ผิด

3
Deploy Frontend (npm run build + wrangler pages deploy + browser test)

Deploy Vite + React Frontend ขึ้น Cloudflare Pages:

  1. Build Frontend:
    • รัน npm run build จากโฟลเดอร์ bookeasy
    • ตรวจสอบว่าไม่มี Error ใดๆ และโฟลเดอร์ dist/ ถูกสร้างขึ้น
    • Vite จะอ่าน .env.production อัตโนมัติ ทำให้ API URL ในโค้ดชี้ไป Workers จริง
  2. Deploy บน Cloudflare Pages:
    • รัน npx wrangler pages deploy dist --project-name bookeasy
    • บันทึก URL ที่ได้รับ เช่น https://bookeasy.pages.dev
  3. ทดสอบในเบราว์เซอร์:
    • เปิด 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

4
เขียน Test 1 ชุด (Vitest + formatPrice + npm test)

เพิ่ม Testing ให้กับโปรเจกต์ BookEasy โดยเริ่มจาก Utility Function:

  1. ติดตั้ง Vitest:
    • รัน npm install -D vitest ในโฟลเดอร์ bookeasy
    • เพิ่ม "test": "vitest run" ใน package.json ภายใต้ scripts
  2. สร้าง Utility Function และ Test File:
    • สร้าง src/utils/formatPrice.js ตาม Snippet 2 ด้านบน
    • สร้าง src/utils/formatPrice.test.js พร้อม Test Cases อย่างน้อย 3 กรณี
  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)