สร้าง Supabase Project#
- สมัครผ่านลิงก์: https://supabase.com ↗ เพื่อรับฐานข้อมูล PostgreSQL ฟรี
- เมื่อสร้างเสร็จ จะได้ Project URL และ Connection String
1. วัตถุประสงค์#
บทความนี้:
- สร้าง RESTful API ด้วย Bun Runtime และ Elysia Framework
- ใช้ Prisma 7 เป็น ORM เชื่อมต่อฐานข้อมูล
- เชื่อมต่อ Supabase PostgreSQL เป็นฐานข้อมูล
- สร้าง CRUD API สำหรับจัดการข้อมูล Product
- ใช้ PrismaBox สร้าง TypeBox Schema อัตโนมัติสำหรับ Validation
- Deploy ขึ้น Vercel ด้วย Bun Runtime
2. เกริ่นนำ: Bun, Elysia และ Prisma 7 คืออะไร?#
2.1 Bun คืออะไร?#
Bun คือ JavaScript Runtime ตัวใหม่ที่เขียนด้วย Zig ที่อ้างตัวว่า:
- 🚀 เร็วกว่า Node.js ถึง 3-4 เท่า
- 📦 มี Built-in Package Manager, Test Runner, Bundler
- 🔥 รองรับ TypeScript โดยไม่ต้อง Compile ล่วงหน้า
- 🎯 ใช้ API เดียวกับ Node.js ส่วนใหญ่ (Drop-in Replacement)
2.2 Elysia คืออะไร?#
Elysia คือ Web Framework สำหรับ Bun ที่:
- ⚡ รวดเร็วและมี Performance สูง
- 📘 Type-safe ด้วย TypeScript
- 🎨 Syntax สวยงาม อ่านง่าย
- 🔧 มี Validation ในตัวด้วย TypeBox
2.3 Prisma 7 มีอะไรใหม่?#
Prisma 7 เปลี่ยนรูปแบบการเชื่อมต่อฐานข้อมูล:
- 🔌 ใช้ Driver Adapter แทน URL ใน Schema
- 🛠️ ต้องติดตั้ง Adapter แยก เช่น
@prisma/adapter-pgสำหรับ PostgreSQL - 📝 ใช้
prisma.config.tsแทนการตั้งค่าใน Schema
3. ติดตั้งเครื่องมือที่จำเป็น#
3.1 ติดตั้ง Bun#
Bun เป็น JavaScript Runtime ที่เร็วและมีประสิทธิภาพสูง ติดตั้งได้ง่ายดาย:
Windows:
powershell -c "irm bun.sh/install.ps1|iex"bashmacOS / Linux:
curl -fsSL https://bun.sh/install | bashbashติดตั้งด้วย Homebrew (macOS / Linux):
brew install oven-sh/bun/bunbashอ้างอิง: Bun Installation Guide ↗
3.2 ตรวจสอบการติดตั้ง#
ตรวจสอบว่า Bun ติดตั้งสำเร็จ:
bun --versionbashเมื่อติดตั้งสำเร็จ จะแสดงเวอร์ชันเช่น 1.x.x
3.3 เครื่องมืออื่นที่แนะนำ#
- Git - สำหรับจัดการเวอร์ชันโค้ด: git-scm.com ↗
- VS Code - โค้ดエดิทเตอร์ที่แนะนำ: code.visualstudio.com ↗
- Supabase Account - สมัครฟรี: supabase.com ↗
4. สร้างโปรเจกต์ด้วย Bun#
4.1 สร้างโปรเจกต์ใหม่#
สร้างโปรเจกต์ใหม่และเข้าไปยังโฟลเดอร์โปรเจกต์
mkdir bun-elysia-crud
cd bun-elysia-crud
bun init -ybash4.2 ติดตั้ง Dependencies#
ติดตั้ง Elysia, Prisma 7 และ PostgreSQL Adapter
bun add elysia
bun add @prisma/client @prisma/adapter-pg pg
bun add -d prismabash5. ตั้งค่า Prisma 7#
5.1 สร้าง Prisma Schema#
สร้าง Schema และ Config สำหรับ Prisma 7
bunx prisma initbashแก้ไขไฟล์ prisma/schema.prisma:
กำหนดโครงสร้างตาราง Product
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model Product {
id String @id @default(cuid())
name String @unique
detail String
price Decimal?
}prisma5.2 สร้าง Prisma Config#
สร้างไฟล์ prisma.config.ts ที่ root ของโปรเจกต์:
Config สำหรับ Prisma 7 ที่จัดการ Environment Variables และ Migrations
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
seed: 'bun ./prisma/seed.ts',
},
datasource: {
url: process.env["DATABASE_URL"],
},
});typescriptหมายเหตุ: สำหรับการ
db pushหรือmigrateให้แก้ไข.envชั่วคราว:bash# เปลี่ยนเป็น DIRECT_URL ตอน db push DATABASE_URL="postgresql://postgres.xxx:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:5432/postgres" # เสร็จแล้วเปลี่ยนกลับเป็น PgBouncer URL DATABASE_URL="postgresql://postgres.xxx:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?pgbouncer=true"
5.3 ตั้งค่า Environment Variables#
สร้างไฟล์ .env:
Connection String สำหรับ Supabase (ผ่าน PgBouncer)
DATABASE_URL="postgresql://postgres.xxx:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?pgbouncer=true"plaintextหมายเหตุ:
DATABASE_URLใช้สำหรับ Application (ผ่าน PgBouncer port 6543)- ตอน
db pushให้เปลี่ยน port เป็น 5432 (Direct URL) ชั่วคราว แล้วเปลี่ยนกลับ
5.4 Generate Prisma Client#
สร้างไฟล์ Prisma Client สำหรับใช้งานใน TypeScript
bunx prisma generatebash5.5 Push Schema ไปยัง Supabase#
สร้างตารางใน Supabase ตาม Schema ที่กำหนด
bunx prisma db pushbashตรวจสอบบน Supabase Table Editor จะเห็นตาราง Product ถูกสร้างขึ้น
6. ติดตั้ง PrismaBox (TypeBox Generator)#
6.1 ติดตั้ง PrismaBox#
ติดตั้ง PrismaBox สำหรับสร้าง TypeBox Schema อัตโนมัติ
bun add -d prismaboxbash6.2 ตั้งค่า PrismaBox#
แก้ไขไฟล์ prisma/schema.prisma เพิ่ม generator สำหรับ PrismaBox:
เพิ่ม PrismaBox Generator เพื่อสร้าง TypeBox Schema
generator client {
provider = "prisma-client-js"
output = "../src/generated/prisma"
}
datasource db {
provider = "postgresql"
}
generator prismabox {
provider = "prismabox"
typeboxImportDependencyName = "elysia"
typeboxImportVariableName = "t"
inputModel = true
output = "../generated/prismabox"
}
model Product {
id String @id @default(cuid())
name String @unique
detail String
price Decimal?
}prisma6.3 Generate PrismaBox#
สร้าง TypeBox Schema จาก Prisma Model
bunx prisma generatebashจะได้ไฟล์ generated/prismabox/Product.ts ที่มี Schema สำหรับ Validation
7. สร้าง CRUD API ด้วย Elysia#
สร้างไฟล์ src/index.ts:
สร้าง RESTful API พร้อม Validation ด้วย PrismaBox Schema
import { Elysia, t } from 'elysia'
import { PrismaPg } from '@prisma/adapter-pg'
import { Pool } from 'pg'
import { PrismaClient } from '../generated/prisma/client'
import {
ProductPlain,
ProductPlainInputCreate,
ProductPlainInputUpdate
} from '../generated/prismabox/Product'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
const prisma = new PrismaClient({ adapter })
// แปลง Decimal เป็น number สำหรับ JSON response
const toResponse = (product: { id: string; name: string; detail: string; price: { toNumber: () => number } | null }) => ({
id: product.id,
name: product.name,
detail: product.detail,
price: product.price?.toNumber() ?? null
})
const app = new Elysia()
// CREATE - สร้างสินค้าใหม่
.post(
'/products',
async ({ body }) => {
const product = await prisma.product.create({ data: body })
return toResponse(product)
},
{
body: ProductPlainInputCreate,
response: ProductPlain
}
)
// READ - ดึงสินค้าทั้งหมด
.get(
'/products',
async () => {
const products = await prisma.product.findMany()
return products.map(toResponse)
},
{
response: t.Array(ProductPlain)
}
)
// READ - ดึงสินค้าตาม ID
.get(
'/products/:id',
async ({ params: { id }, status }) => {
const product = await prisma.product.findUnique({ where: { id } })
if (!product) return status(404, 'Product not found')
return toResponse(product)
},
{
response: {
200: ProductPlain,
404: t.String()
}
}
)
// UPDATE - อัปเดตสินค้า
.patch(
'/products/:id',
async ({ params: { id }, body, set }) => {
try {
const product = await prisma.product.update({
where: { id },
data: body
})
return toResponse(product)
} catch {
set.status = 404
return 'Product not found'
}
},
{
body: ProductPlainInputUpdate,
response: {
200: ProductPlain,
404: t.String()
}
}
)
// DELETE - ลบสินค้า
.delete(
'/products/:id',
async ({ params: { id }, set }) => {
try {
await prisma.product.delete({ where: { id } })
return { message: 'Product deleted' }
} catch {
set.status = 404
return { message: 'Product not found' }
}
},
{
response: t.Object({ message: t.String() })
}
)
// For Vercel: export as default
export default app
// For local dev: start server
if (import.meta.main || process.env.NODE_ENV !== 'production') {
app.listen(3000)
console.log(
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
)
}typescript8. สร้าง Seed Data#
สร้างไฟล์ prisma/seed.ts:
สร้างข้อมูลตัวอย่างสำหรับทดสอบ
import { PrismaClient } from '../generated/prisma/client'
import { PrismaPg } from '@prisma/adapter-pg'
import { Pool } from 'pg'
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
const prisma = new PrismaClient({ adapter })
async function main() {
console.log('Start seeding...')
await prisma.product.createMany({
data: [
{
name: 'Wireless Headphones',
detail: 'Noise-cancelling over-ear headphones with 30hr battery life',
price: 299.99,
},
{
name: 'Mechanical Keyboard',
detail: 'RGB backlit mechanical keyboard with brown switches',
price: 149.50,
},
{
name: 'USB-C Hub',
detail: '7-in-1 multiport adapter with HDMI, USB 3.0, and SD card reader',
price: 49.99,
},
{
name: 'Laptop Stand',
detail: 'Aluminum adjustable laptop stand for better ergonomics',
price: 79.00,
},
{
name: 'Wireless Mouse',
detail: 'Ergonomic wireless mouse with precision tracking',
price: 35.99,
},
],
skipDuplicates: true,
})
console.log('Seeding finished.')
}
main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})typescriptรัน seed data
bun run seedbashอัปเดต package.json เพิ่ม script สำหรับทดสอบ:
9. รันและทดสอบ API#
เพิ่ม script สำหรับรัน development server
{
"scripts": {
"dev": "bun run --watch src/index.ts"
}
}json9.1 เริ่ม Server#
รัน Development Server ด้วย Bun
bun run devbashServer จะทำงานที่ http://localhost:3000
9.2 ทดสอบ API#
สำหรับ Linux/macOS - ใช้ cURL
CREATE - สร้างสินค้าใหม่
curl -X POST http://localhost:3000/products \
-H "Content-Type: application/json" \
-d '{"name":"Gaming Mouse","detail":"RGB gaming mouse with 16000 DPI","price":89.99}'bashREAD - ดึงสินค้าทั้งหมด
curl http://localhost:3000/productsbashREAD - ดึงสินค้าตาม ID
curl http://localhost:3000/products/[PRODUCT_ID]bashUPDATE - อัปเดตสินค้า
curl -X PATCH http://localhost:3000/products/[PRODUCT_ID] \
-H "Content-Type: application/json" \
-d '{"price":79.99}'bashDELETE - ลบสินค้า
curl -X DELETE http://localhost:3000/products/[PRODUCT_ID]bashสำหรับ Windows - ใช้ PowerShell
CREATE - สร้างสินค้าใหม่
Invoke-RestMethod -Uri http://localhost:3000/products -Method POST -ContentType "application/json" -Body '{"name":"Gaming Mouse","detail":"RGB gaming mouse with 16000 DPI","price":89.99}'powershellREAD - ดึงสินค้าทั้งหมด
Invoke-RestMethod -Uri http://localhost:3000/products -Method GETpowershellREAD - ดึงสินค้าตาม ID
Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method GETpowershellUPDATE - อัปเดตสินค้า
Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method PATCH -ContentType "application/json" -Body '{"price":79.99}'powershellDELETE - ลบสินค้า
Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method DELETEpowershell9.3 ทดสอบด้วย Supabase Table Editor#
เปิด Supabase Dashboard → Table Editor → เลือกตาราง Product จะเห็นข้อมูลที่สร้างจาก API
10. Deploy ไปยัง Vercel ด้วย Bun Runtime#
Vercel รองรับ Bun Runtime สำหรับ deploy backend applications แล้ว (Public Beta)
10.1 สร้างไฟล์ vercel.json#
สร้างไฟล์ vercel.json ที่ root ของโปรเจกต์:
Config สำหรับ Vercel พร้อม Bun Runtime
{
"buildCommand": "bunx prisma generate",
"bunVersion": "1.x",
"rewrites": [{ "source": "/(.*)", "destination": "/src" }]
}json10.2 สร้างไฟล์ tsconfig.json#
สร้างไฟล์ tsconfig.json ที่ root ของโปรเจกต์ (จำเป็นสำหรับ Vercel):
{
"compilerOptions": {
"target": "ES2021",
"module": "ES2022",
"moduleResolution": "node",
"types": ["bun-types"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}json10.3 ตั้งค่า Environment Variables บน Vercel#
- Push code ไปยัง GitHub
- Import project บน Vercel ↗
- ไปที่ Settings → Environment Variables
- เพิ่ม
DATABASE_URL:
DATABASE_URL = postgresql://postgres.xxx:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?pgbouncer=trueplaintextใช้ PgBouncer URL (port 6543) สำหรับ Vercel เพื่อประสิทธิภาพที่ดีกว่า
10.4 Deploy#
Deploy ด้วย Vercel CLI
bunx vercelbashหรือ auto-deploy เมื่อ push ไปยัง GitHub branch main
10.5 ตรวจสอบ Deployment#
เมื่อ deploy สำเร็จ จะได้ URL เช่น:
https://elysia-api-test.vercel.apphttps://elysia-api-test.vercel.app/productshttps://elysia-api-test.vercel.app/products/:id
11. API Endpoints สรุป#
| Method | Route | Description |
|---|---|---|
| POST | /products | สร้างสินค้าใหม่ |
| GET | /products | ดึงสินค้าทั้งหมด |
| GET | /products/:id | ดึงสินค้าตาม ID |
| PATCH | /products/:id | อัปเดตสินค้า |
| DELETE | /products/:id | ลบสินค้า |
12. สรุป#
ข้อดีของ Stack นี้:
- 🚀 Bun เร็วกว่า Node.js
- 📘 Elysia มี Type-safe Validation ในตัว
- 🎨 PrismaBox ลดการเขียน Schema ซ้ำ
- 💾 Supabase ฟรีและใช้งานง่าย
แหล่งอ้างอิง: