Deployment
ระบบ deploy แบบ 3-tier serverless-friendly — frontend บน Cloudflare Pages, backend บน Render.com, database บน Supabase, object storage บน Cloudflare R2
Architecture
┌──────────────────────────────────────────────┐
│ GitHub (master branch) │
└────────┬─────────────────────────┬───────────┘
│ │
│ webhook │ wrangler CLI
│ (auto) │ (manual / GH Action)
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ Render.com │ │ Cloudflare Pages │
│ NestJS API │ │ - wms-dev │
│ Singapore │ │ - wms-admin │
│ Free tier │ │ - wms-docs │
└────────┬───────┘ └────────┬─────────┘
│ │
│ HTTPS (CORS) │ Static assets
│ │
▼ ▼
┌────────────────────────────────────────┐
│ End users / browsers │
└────────────────────────────────────────┘
│
│ Prisma (pooled)
▼
┌────────────────────────────────────────┐
│ Supabase (PostgreSQL 16, PgBouncer) │
└────────────────────────────────────────┘Frontend → Cloudflare Pages
3 separate Pages projects:
| Project | Source | Build command | Output dir |
|---|---|---|---|
wms-dev | apps/web/ | pnpm --filter @wms/web build | apps/web/dist/ |
wms-admin | apps/admin/ | pnpm --filter @wms/admin build | apps/admin/dist/ |
wms-docs | apps/docs/ | pnpm --filter @wms/docs build | apps/docs/.vitepress/dist/ |
Branch = environment
main(หรือmaster) → production deployment ที่*.pages.dev- branch อื่น ๆ → preview deployment (URL auto-generated)
Deploy commands (CLI)
# install wrangler ถ้ายังไม่มี
npm install -g wrangler
# manual deploy (จาก root)
pnpm --filter @wms/web build
wrangler pages deploy apps/web/dist --project-name=wms-dev --branch=main
pnpm --filter @wms/admin build
wrangler pages deploy apps/admin/dist --project-name=wms-admin --branch=main
pnpm --filter @wms/docs build
wrangler pages deploy apps/docs/.vitepress/dist --project-name=wms-docs --branch=mainEnv vars สำหรับ frontend (apps/{web,admin}/.env.production)
VITE_API_URL=https://wms-api-dev-2w6s.onrender.com(prefix VITE_ จำเป็นเพื่อให้ Vite expose สู่ client bundle)
Backend → Render.com
render.yaml (infra-as-code):
services:
- type: web
name: wms-api-dev
runtime: node
region: singapore
plan: free
rootDir: .
buildCommand: |
npm install -g pnpm &&
pnpm install &&
cd apps/api &&
npx prisma generate &&
pnpm build
startCommand: cd apps/api && node dist/main
envVars:
- key: NODE_ENV
value: development
- key: DATABASE_URL
sync: false
- key: DIRECT_URL
sync: false
- key: JWT_SECRET
sync: false
- key: CORS_ORIGIN
sync: falseFree tier behavior
- Auto-sleep หลัง 15 นาทีไม่มี traffic → request แรกหลังหลับจะรอ ~30 วินาที (cold start)
- ดูวิธีจัดการ: Cold Start Troubleshooting
- เพื่อหลีกเลี่ยง → upgrade เป็น Starter plan
Auto-deploy
Render listen GitHub webhook — push to master → auto build + redeploy
Required env vars
จาก app.module.ts (Joi validation):
| Env var | Required | Default | คำอธิบาย |
|---|---|---|---|
DATABASE_URL | ✅ | — | Supabase pooled (port 6543) |
DIRECT_URL | ✅ | — | Supabase direct (port 5432) — สำหรับ migrations |
JWT_SECRET | ✅ | — | ≥ 16 ตัว — app refuse to start ถ้าไม่ตั้ง |
JWT_ACCESS_TTL | — | 900 | access token TTL (วินาที) |
JWT_REFRESH_TTL | — | 604800 | refresh token TTL |
APP_PORT | — | 3000 | (Render override ผ่าน PORT) |
APP_TIER | — | S | S/M/L — กำหนด feature gating |
NODE_ENV | — | development | development/uat/production/test |
CORS_ORIGIN | — | http://localhost:5173 | comma-separated |
R2_ENDPOINT | — | '' | Cloudflare R2 endpoint |
R2_ACCESS_KEY_ID | — | '' | R2 access key |
R2_SECRET_ACCESS_KEY | — | '' | R2 secret |
R2_BUCKET_NAME | — | wms-files | bucket |
port binding
Render inject PORT env var ตอนรัน — main.ts ใช้ก่อน fallback APP_PORT:
const port = config.get<number>('PORT') || config.get<number>('APP_PORT', 3000);Database → Supabase
- PostgreSQL 16 managed
- Region:
aws-1-ap-northeast-2(Seoul) — ใกล้ Singapore ที่สุดในแถว free tier - 2 URLs:
- DATABASE_URL — pooled (PgBouncer port 6543) — ใช้ runtime
- DIRECT_URL — direct (port 5432) — ใช้ Prisma migrate/introspect
- 3 separate Supabase projects แนะนำ (dev / uat / prod) — เพื่อแยก data
Schema management
# apply schema ตรงๆ ไม่สร้าง migration file (สำหรับ Supabase)
pnpm db:push # = prisma db push
# สร้าง migration file (local dev เท่านั้น)
pnpm db:migrate # = prisma migrate dev
# regen Prisma Client หลัง schema เปลี่ยน
pnpm db:generate # = prisma generateระบบใช้ db push workflow ใน Supabase (ไม่ commit migration files) — เหมาะกับทีมเล็กที่ schema เปลี่ยนบ่อย
CI/CD Flow
Developer
│
│ git push origin master
▼
┌──────────────┐
│ GitHub │
└──────┬───────┘
│
├─── webhook ───────► ┌──────────────┐
│ │ Render │ ──► backend redeploy
│ │ (auto) │ (~5 min build + deploy)
│ └──────────────┘
│
└─── manual ──────► wrangler pages deploy ──► ┌──────────────────┐
│ Cloudflare Pages │
│ wms-dev │
│ wms-admin │
│ wms-docs │
└──────────────────┘ฝั่ง Cloudflare Pages สามารถผูก GitHub repo + auto-build ได้เหมือนกัน — แต่ปัจจุบันใช้ manual wrangler เพื่อให้ control timing ของ deploy
Local Development Stack (Optional)
ถ้าไม่อยากใช้ Supabase ตอน dev — ใช้ docker-compose.yml:
docker compose up -d # start postgres + redis + apiดู Local Development สำหรับรายละเอียด
Health Check
curl https://wms-api-dev-2w6s.onrender.com/api/v1/health/live
# {"status":"ok"}
curl https://wms-api-dev-2w6s.onrender.com/api/v1/health/ready
# {"status":"ok","db":"connected"}
curl https://wms-api-dev-2w6s.onrender.com/api/v1/health/stats
# { uptime, memory, dbResponseTime, ... }Render ตั้ง health check path → /api/v1/health/live ก็พอ