Back

สร้าง CRUD API ด้วย Bun + Elysia + Prisma 7 + SupabaseBlur image

สร้าง 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"
bash

macOS / Linux:

curl -fsSL https://bun.sh/install | bash
bash

ติดตั้งด้วย Homebrew (macOS / Linux):

brew install oven-sh/bun/bun
bash

อ้างอิง: Bun Installation Guide

3.2 ตรวจสอบการติดตั้ง#

ตรวจสอบว่า Bun ติดตั้งสำเร็จ:

bun --version
bash

เมื่อติดตั้งสำเร็จ จะแสดงเวอร์ชันเช่น 1.x.x

3.3 เครื่องมืออื่นที่แนะนำ#


4. สร้างโปรเจกต์ด้วย Bun#

4.1 สร้างโปรเจกต์ใหม่#

สร้างโปรเจกต์ใหม่และเข้าไปยังโฟลเดอร์โปรเจกต์

mkdir bun-elysia-crud
cd bun-elysia-crud
bun init -y
bash

4.2 ติดตั้ง Dependencies#

ติดตั้ง Elysia, Prisma 7 และ PostgreSQL Adapter

bun add elysia
bun add @prisma/client @prisma/adapter-pg pg
bun add -d prisma
bash

5. ตั้งค่า Prisma 7#

5.1 สร้าง Prisma Schema#

สร้าง Schema และ Config สำหรับ Prisma 7

bunx prisma init
bash

แก้ไขไฟล์ 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?
}
prisma

5.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 ชั่วคราว:

# เปลี่ยนเป็น 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"
bash

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 generate
bash

5.5 Push Schema ไปยัง Supabase#

สร้างตารางใน Supabase ตาม Schema ที่กำหนด

bunx prisma db push
bash

ตรวจสอบบน Supabase Table Editor จะเห็นตาราง Product ถูกสร้างขึ้น


6. ติดตั้ง PrismaBox (TypeBox Generator)#

6.1 ติดตั้ง PrismaBox#

ติดตั้ง PrismaBox สำหรับสร้าง TypeBox Schema อัตโนมัติ

bun add -d prismabox
bash

6.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?
}
prisma

6.3 Generate PrismaBox#

สร้าง TypeBox Schema จาก Prisma Model

bunx prisma generate
bash

จะได้ไฟล์ 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}`
  )
}
typescript

8. สร้าง 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 seed
bash

อัปเดต package.json เพิ่ม script สำหรับทดสอบ:


9. รันและทดสอบ API#

เพิ่ม script สำหรับรัน development server

{
  "scripts": {
    "dev": "bun run --watch src/index.ts"
  }
}
json

9.1 เริ่ม Server#

รัน Development Server ด้วย Bun

bun run dev
bash

Server จะทำงานที่ 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}'
bash

READ - ดึงสินค้าทั้งหมด

curl http://localhost:3000/products
bash

READ - ดึงสินค้าตาม ID

curl http://localhost:3000/products/[PRODUCT_ID]
bash

UPDATE - อัปเดตสินค้า

curl -X PATCH http://localhost:3000/products/[PRODUCT_ID] \
  -H "Content-Type: application/json" \
  -d '{"price":79.99}'
bash

DELETE - ลบสินค้า

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}'
powershell

READ - ดึงสินค้าทั้งหมด

Invoke-RestMethod -Uri http://localhost:3000/products -Method GET
powershell

READ - ดึงสินค้าตาม ID

Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method GET
powershell

UPDATE - อัปเดตสินค้า

Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method PATCH -ContentType "application/json" -Body '{"price":79.99}'
powershell

DELETE - ลบสินค้า

Invoke-RestMethod -Uri http://localhost:3000/products/[PRODUCT_ID] -Method DELETE
powershell

9.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" }]
}
json

10.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
  }
}
json

10.3 ตั้งค่า Environment Variables บน Vercel#

  1. Push code ไปยัง GitHub
  2. Import project บน Vercel
  3. ไปที่ Settings → Environment Variables
  4. เพิ่ม DATABASE_URL:
DATABASE_URL = postgresql://postgres.xxx:[PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?pgbouncer=true
plaintext

ใช้ PgBouncer URL (port 6543) สำหรับ Vercel เพื่อประสิทธิภาพที่ดีกว่า

10.4 Deploy#

Deploy ด้วย Vercel CLI

bunx vercel
bash

หรือ auto-deploy เมื่อ push ไปยัง GitHub branch main

10.5 ตรวจสอบ Deployment#

เมื่อ deploy สำเร็จ จะได้ URL เช่น:

  • https://elysia-api-test.vercel.app
  • https://elysia-api-test.vercel.app/products
  • https://elysia-api-test.vercel.app/products/:id

11. API Endpoints สรุป#

MethodRouteDescription
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 ฟรีและใช้งานง่าย

แหล่งอ้างอิง:

สร้าง CRUD API ด้วย Bun + Elysia + Prisma 7 + Supabase
Author กานต์ ยงศิริวิทย์ / Karn Yongsiriwit
Published at January 10, 2026

Loading comments...

Comments 0