Deploy บน Vercel ด้วย Postgres และ R2
สิ่งที่จะได้
เว็บ Next.js ที่มี Easy CMS บน Vercel: เนื้อหาอยู่ใน Postgres ที่ host ไว้ ไฟล์อัปโหลดอยู่ใน Cloudflare R2 และมี cron สำหรับการเผยแพร่ตามเวลา ใช้: Next.js, ฐานข้อมูล, อัปโหลด, migration
function แบบ serverless ไม่มีดิสก์ที่อยู่ถาวร SQLite และไฟล์อัปโหลดในเครื่องจึงใช้ไม่ได้ ให้ใช้ Postgres และที่เก็บไฟล์แบบ S3 แทน
1. Postgres
สร้างฐานข้อมูลบน Postgres แบบ host ไว้ที่ไหนก็ได้ (Neon, Supabase, marketplace ของ Vercel…) แล้วคัดลอก connection string
npm install @easy-cms/db-postgrespnpm add @easy-cms/db-postgresyarn add @easy-cms/db-postgresbun add @easy-cms/db-postgresimport { postgres } from '@easy-cms/db-postgres'
export default defineConfig({
// …
// Vercel: ฐานข้อมูลที่ host ไว้ ส่วนเครื่องคุณ: PGlite (Postgres ในโฟลเดอร์) การพัฒนาและ migration
// จึงใช้ SQL แบบเดียวกับ production
db: process.env.DATABASE_URL
? postgres({ url: process.env.DATABASE_URL, max: 2 }) // pool เล็กๆ ต่อ function instance
: postgres({ pglite: '.pglite' }),
})สำหรับ PGlite ตอนพัฒนา: npm install @electric-sql/pglite (create-easy-cms --db postgres ติดตั้งให้แล้ว)
Connection pooler
ใช้ connection string แบบต่อตรงของฐานข้อมูล pooler แบบ transaction mode อาจไม่รองรับ prepared statement ที่ driver ใช้ ถ้าจะเปลี่ยนไปใช้ ให้ทดสอบก่อน
2. Cloudflare R2 สำหรับไฟล์อัปโหลด
สร้าง bucket และ API token (Object Read & Write) ใน Cloudflare แล้ว:
npm install @easy-cms/storage-s3pnpm add @easy-cms/storage-s3yarn add @easy-cms/storage-s3bun add @easy-cms/storage-s3import { s3Storage } from '@easy-cms/storage-s3'
upload: {
storage: s3Storage({
bucket: process.env.R2_BUCKET as string,
region: 'auto',
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
}),
},ไฟล์จะถูกส่งผ่าน CMS API เว้นแต่คุณตั้ง URL สาธารณะของ bucket (publicUrl) ดู S3, Cloudflare R2 และ MinIO
3. Migration ทุกครั้งที่ deploy
สร้าง migration ในเครื่อง (ใช้ PGlite ไม่ต้องมี DATABASE_URL) แล้ว commit:
npx easy-cms migrate:create init
git add easy-cms/migrationspnpm exec easy-cms migrate:create init
git add easy-cms/migrationsyarn easy-cms migrate:create init
git add easy-cms/migrationsbunx easy-cms migrate:create init
git add easy-cms/migrationsแล้วรันก่อน build ทุกครั้งใน package.json:
{ "scripts": { "build": "easy-cms migrate && next build" } }Vercel รัน npm run build ทุกครั้งที่ deploy ฐานข้อมูลจึงถูก migrate ก่อนที่เวอร์ชันใหม่จะรับ request
4. Environment variable
ใน Project Settings → Environment Variables:
| ชื่อ | ค่า |
|---|---|
EASY_CMS_SECRET | ค่าสุ่มใหม่: openssl rand -hex 32 |
DATABASE_URL | connection string ของ Postgres |
R2_BUCKET, R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY | จาก Cloudflare |
CRON_SECRET | ค่าสุ่มอีกค่า สำหรับ cron ด้านล่าง |
5. Cron สำหรับบทความที่ตั้งเวลาและ webhook ที่ต้องส่งซ้ำ
บน Vercel ไม่มี process ที่ทำงานค้างไว้ ให้ Vercel Cron เรียก endpoint ของงานแทน Vercel ส่ง Authorization: Bearer $CRON_SECRET ซึ่ง Easy CMS ตรวจให้
{
"crons": [{ "path": "/api/cms/jobs/run", "schedule": "*/5 * * * *" }]
}บทความที่ตั้งเวลาไว้จะขึ้นเว็บภายในห้านาทีหลังเวลาที่ตั้ง ตรวจดูว่าแพ็กเกจ Vercel ของคุณให้ cron รันถี่แค่ไหน
6. IP ของผู้ใช้สำหรับการจำกัด login
Vercel ตั้ง X-Forwarded-For ให้ ให้เชื่อค่านี้ใน route ของ API:
export const { GET, HEAD, POST, PATCH, PUT, DELETE, OPTIONS } = createRouteHandlers(config, {
trustProxy: true,
})ตรวจสอบ
deploy แล้วเปิด https://your-site.vercel.app/admin สร้าง admin คนแรก อัปโหลดรูป (ไฟล์จะไปอยู่ใน bucket) และเผยแพร่บทความ