1. Supabase คืออะไร
Supabase คือ Open-Source Backend-as-a-Service ที่รวม PostgreSQL, Auth, Storage, Realtime, และ Edge Functions ไว้ใน Platform เดียว บางครั้งเรียกว่า "Firebase Alternative" เพราะให้บริการคล้ายกันแต่สร้างบน PostgreSQL แทน NoSQL
สิ่งที่ทำให้ Supabase โดดเด่น:
- PostgreSQL — ฐานข้อมูลเชิงสัมพันธ์ที่ทรงพลัง รองรับ SQL มาตรฐาน, JSON, Full-Text Search, และ Extension หลายร้อยตัว
- Auth — ระบบ Authentication พร้อมใช้ รองรับ Email/Password, OAuth (Google, GitHub), Magic Link โดยไม่ต้องเขียนโค้ดใหม่
- Storage — อัปโหลดและจัดการไฟล์ รูปภาพ และวิดีโอ มี CDN ในตัว
- Realtime — Subscribe การเปลี่ยนแปลงข้อมูลใน Database แบบ Real-time ผ่าน WebSocket
- Auto-generated API — สร้าง REST API และ GraphQL API จากโครงสร้างตารางอัตโนมัติ พร้อม SDK สำหรับ JavaScript, Python, Flutter, Swift
สถาปัตยกรรม Supabase:
┌─────────────────────────────────────────────┐
│ Supabase Platform │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │PostgreSQL│ │ Auth │ │ Storage │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Realtime │ │PostgREST │ │ Edge Fn │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘
│
supabase-js SDK
│
┌────────────────────────────────┐
│ Frontend (React / Vanilla) │
└────────────────────────────────┘
2. การสร้างโปรเจกต์ใน Supabase Dashboard
การเริ่มต้นใช้ Supabase ทำได้ผ่าน Dashboard ที่ supabase.com โดยไม่จำเป็นต้องติดตั้งอะไรบน Machine ของตัวเอง เพราะฐานข้อมูลและ API รันบน Cloud ทั้งหมด
ขั้นตอนการสร้างโปรเจกต์ใหม่:
-
สมัครบัญชีที่
supabase.com(ใช้ GitHub Account ได้) แล้วคลิก New Project - ตั้งชื่อโปรเจกต์ กำหนด Database Password ที่แข็งแกร่ง และเลือก Region (แนะนำ Southeast Asia (Singapore) สำหรับผู้ใช้ในไทย)
- รอประมาณ 1–2 นาทีให้ Supabase สร้าง PostgreSQL Instance ให้
-
ไปที่ Settings → API บันทึก
Project URLและanon keyไว้ใช้ในโค้ด JavaScript
Supabase Free Tier ให้ 2 โปรเจกต์ฟรี, Storage 500 MB, และ Database 500 MB เพียงพอสำหรับโปรเจกต์ในคอร์สนี้ หากโปรเจกต์ไม่มีการใช้งานเกิน 7 วัน จะถูก Pause อัตโนมัติ สามารถ Resume ได้ผ่าน Dashboard
3. PostgreSQL พื้นฐาน — ความแตกต่างจาก MySQL
PostgreSQL เป็น Relational Database ที่มีความสามารถสูงกว่า MySQL หลายด้าน Supabase เลือก PostgreSQL เพราะรองรับ Feature ขั้นสูงที่จำเป็นสำหรับ Modern Web Application
ความแตกต่างสำคัญที่ควรทราบ:
-
UUID — PostgreSQL ใช้
UUIDเป็น Primary Key แทนINT AUTO_INCREMENTของ MySQL UUID สร้างด้วยgen_random_uuid()ทำให้ ID ไม่เดาได้และ Merge ข้อมูลจากหลาย Database ได้ -
TIMESTAMPTZ — PostgreSQL ใช้
TIMESTAMPTZ(Timestamp with Time Zone) แทนTIMESTAMPของ MySQL เพื่อให้เก็บ Timezone ไว้ด้วยเสมอ -
CHECK Constraint — PostgreSQL รองรับ CHECK Constraint ที่ซับซ้อนโดยตรงใน
Column Definition เช่น
CHECK (status IN ('pending','confirmed','cancelled')) -
Extensions — PostgreSQL มี Extension เสริมเช่น
uuid-ossp,pgvector(AI/Vector Search),postgis(GIS/Location Data) - Row Level Security (RLS) — PostgreSQL รองรับ Policy ความปลอดภัยระดับแถว ซึ่งเป็นหัวใจสำคัญของ Supabase ในการจำกัดการเข้าถึงข้อมูล
เปรียบเทียบ MySQL กับ PostgreSQL ใน Supabase:
Feature MySQL PostgreSQL (Supabase)
──────────────────────────────────────────────────────────
Primary Key INT AUTO_INCREMENT UUID DEFAULT gen_random_uuid()
Timestamp TIMESTAMP TIMESTAMPTZ
String VARCHAR(255) TEXT (ไม่จำกัดขนาด)
Boolean TINYINT(1) BOOLEAN
JSON JSON JSONB (indexed, ค้นหาได้เร็วกว่า)
Enum ENUM('a','b') CHECK (col IN ('a','b'))
Auth Integration ไม่มี auth.users (built-in)
4. ออกแบบตาราง BookEasy: services, bookings, profiles
ระบบ BookEasy ต้องการตาราง 3 ตาราง ที่มีความสัมพันธ์กันผ่าน Foreign Key:
-
services — เก็บข้อมูลบริการที่ให้จอง เช่น ตัดผม นวด สปา
มี
id, name, description, price, duration_min, image_url, created_at -
bookings — เก็บข้อมูลการจอง เชื่อมกับ
auth.usersและservicesผ่าน Foreign Key มีid, user_id, service_id, booking_date, booking_time, status, notes, created_at -
profiles — เก็บข้อมูลโปรไฟล์ผู้ใช้ที่เพิ่มเติมจาก
auth.usersเชื่อมกับauth.usersแบบ One-to-One มีid, full_name, phone, avatar_url, updated_at
ความสัมพันธ์ระหว่างตาราง: ผู้ใช้ 1 คน (auth.users) มีได้ 1 โปรไฟล์ (profiles)
และมีการจองได้หลายครั้ง (bookings) แต่ละการจองเชื่อมกับบริการ 1 รายการ (services)
ER Diagram — BookEasy:
auth.users (Supabase built-in)
│ id (UUID)
│
├──────────────────────────────┐
│ │
▼ (1:1) ▼ (1:N)
profiles bookings
───────────────── ──────────────────────────
id → auth.users.id id (UUID, PK)
full_name user_id → auth.users.id
phone service_id → services.id
avatar_url booking_date
updated_at booking_time
status (pending/confirmed/cancelled)
notes
created_at
│
│ (N:1)
▼
services
─────────────────
id (UUID, PK)
name
description
price
duration_min
image_url
created_at
5. Row Level Security (RLS) — แนวคิดพื้นฐาน
Row Level Security (RLS) คือ Feature ของ PostgreSQL ที่ให้กำหนด Policy ว่าผู้ใช้แต่ละคนสามารถ SELECT, INSERT, UPDATE, หรือ DELETE แถวไหนได้บ้าง โดยตรวจสอบที่ระดับ Database ไม่ใช่ระดับ Application Code
ทำไม RLS ถึงสำคัญใน Supabase:
- ความปลอดภัยโดย Default — เมื่อเปิด RLS บนตาราง ไม่มีใครเข้าถึงข้อมูลได้เลยจนกว่าจะสร้าง Policy ป้องกันการรั่วไหลข้อมูลหาก Developer ลืมเพิ่ม Filter ใน Query
- ตรวจสอบที่ Database — แม้ผู้ไม่หวังดีจะ Bypass Application Code ได้ RLS ยังคุ้มครองข้อมูลที่ระดับ Database อยู่
-
ใช้ JWT Token — Supabase ส่ง
auth.uid()ซึ่งคือ UUID ของผู้ใช้ปัจจุบัน ไปใน Policy เพื่อเปรียบเทียบ เช่นuser_id = auth.uid()ให้ผู้ใช้เห็นเฉพาะข้อมูลของตัวเอง
ใน Lab 5 นี้ยังไม่เปิด RLS เพื่อให้ทดสอบได้ง่าย แต่ใน Production จำเป็นต้องเปิด RLS ทุกตาราง และเขียน Policy อย่างรอบคอบ มิเช่นนั้นข้อมูลของผู้ใช้ทุกคนจะมองเห็นกันได้
-- snippet 1: สร้างตาราง BookEasy ใน Supabase SQL Editor
-- ตารางบริการ
CREATE TABLE services (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT,
price NUMERIC(10,2) NOT NULL,
duration_min INT NOT NULL,
image_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ตารางการจอง
CREATE TABLE bookings (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
service_id UUID REFERENCES services(id) ON DELETE CASCADE,
booking_date DATE NOT NULL,
booking_time TIME NOT NULL,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending','confirmed','cancelled')),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- ตารางโปรไฟล์ผู้ใช้
CREATE TABLE profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
full_name TEXT,
phone TEXT,
avatar_url TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- snippet 2: เพิ่มข้อมูลทดสอบ
INSERT INTO services (name, description, price, duration_min) VALUES
('ตัดผมชาย', 'ตัดผมสไตล์เกาหลี/ยุโรป พร้อมล้างและเป่า', 150, 45),
('ตัดผมหญิง', 'ตัดและจัดทรงผมสำหรับผู้หญิง', 250, 60),
('นวดแผนไทย', 'นวดผ่อนคลายกล้ามเนื้อด้วยภูมิปัญญาไทย', 400, 60),
('นวดหน้า', 'บำรุงผิวหน้า ลดรอยแดง เพิ่มความชุ่มชื้น', 350, 45),
('สปาหน้า', 'โปรแกรมดูแลผิวหน้าครบวงจร', 600, 90);
-- ดูข้อมูล
SELECT id, name, price, duration_min FROM services ORDER BY price;
// snippet 3: เชื่อมต่อ Supabase ใน JavaScript
// ติดตั้ง: npm install @supabase/supabase-js
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = 'https://xxxx.supabase.co'; // จาก Supabase Dashboard
const supabaseKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI...'; // anon key
export const supabase = createClient(supabaseUrl, supabaseKey);
// ทดสอบดึงข้อมูล
const { data, error } = await supabase
.from('services')
.select('*')
.order('price', { ascending: true });
console.log(data, error);
🧪 ปฏิบัติการ Lab 5 — BookEasy: สร้างฐานข้อมูลใน Supabase
ฝึกสร้างฐานข้อมูล PostgreSQL ใน Supabase Dashboard เขียน SQL สร้างตารางและเพิ่มข้อมูลทดสอบ จากนั้นเชื่อมต่อผ่าน JavaScript Client
ต่อยอดจาก Lab 4 — สร้าง database จริงใน Supabase เพื่อใช้แทน mock data ใน Week 7
สร้างโปรเจกต์ใหม่ใน Supabase Dashboard และบันทึก Credentials ที่จำเป็น
- ไปที่
supabase.com→ Sign In ด้วย GitHub Account → คลิก New Project - ตั้งชื่อโปรเจกต์ว่า
bookeasyกำหนด Database Password และเลือก Region Southeast Asia (Singapore) - รอให้โปรเจกต์พร้อม (ประมาณ 1–2 นาที) จนสถานะเปลี่ยนเป็น "Active"
- ไปที่ Settings → API และบันทึก
Project URLและanon public keyไว้ใช้ใน Task 4
โปรเจกต์ Supabase ชื่อ "bookeasy" สถานะ Active ใน Dashboard มี Project URL และ anon key พร้อมใช้งาน ถ่ายภาพหน้าจอ Settings → API ที่แสดง URL และ Key
Database Password ต้องจำหรือบันทึกไว้ในที่ปลอดภัยเพราะจะใช้หาก Connect ตรงผ่าน psql
แต่สำหรับ JavaScript SDK ใช้ anon key แทน ไม่จำเป็นต้องใช้ Password
ใช้ SQL Editor ใน Supabase Dashboard สร้างตาราง 3 ตารางสำหรับ BookEasy
- ไปที่เมนู SQL Editor ใน Supabase Dashboard แล้วคลิก New Query
- คัดลอก SQL จาก Snippet 1 ด้านบน วางในช่อง Editor แล้วคลิก Run
- ตรวจสอบว่า Query สำเร็จ (ไม่มี Error Message สีแดง)
- ไปที่เมนู Table Editor ตรวจว่ามีตารางครบ 3 ตาราง:
services,bookings,profiles
Table Editor แสดงตาราง 3 ตารางครบ (services, bookings, profiles)
แต่ละตารางมี Column ครบตามที่ออกแบบ ถ่ายภาพหน้าจอ Table Editor ที่แสดงทั้ง 3 ตาราง
หากเกิด Error "relation auth.users does not exist" ให้ตรวจสอบว่ารัน SQL ใน Project ที่ถูกต้อง
auth.users เป็นตาราง Built-in ของ Supabase ที่มีอยู่แล้วในทุกโปรเจกต์
ไม่ต้องสร้างเอง
เพิ่มข้อมูลบริการ 5 รายการลงในตาราง services และตรวจสอบด้วย SELECT
- เปิด SQL Editor → New Query อีกครั้ง
- คัดลอก SQL จาก Snippet 2 ด้านบน (ส่วน INSERT) วางในช่อง Editor แล้วคลิก Run
- รัน
SELECT * FROM services;ในช่อง Editor เพื่อตรวจสอบ - ตรวจว่าผลลัพธ์แสดง 5 แถว พร้อม UUID ที่ระบบสร้างให้อัตโนมัติ
SELECT * FROM services; แสดงผลลัพธ์ 5 แถว ทุกแถวมี id เป็น UUID
และ created_at ที่ระบบกำหนดให้อัตโนมัติ ถ่ายภาพหน้าจอผลลัพธ์ของ SELECT
สามารถดูข้อมูลผ่าน Table Editor ได้เช่นกัน โดยคลิกที่ตาราง services
เพื่อดูในรูปแบบตาราง Excel ที่อ่านง่ายกว่า SQL Query Result
ติดตั้ง Supabase JavaScript SDK ใน bookeasy-api และทดสอบดึงข้อมูลจากตาราง services
- เปิด Terminal ใน folder
bookeasy-apiแล้วรันnpm install @supabase/supabase-js - สร้างไฟล์ใหม่
src/lib/supabase.jsแล้วคัดลอกโค้ดจาก Snippet 3 ด้านบน - แทนค่า
supabaseUrlและsupabaseKeyด้วย URL และ anon key ของโปรเจกต์ตัวเอง (จาก Task 1) - สร้างไฟล์
test-db.jsที่ importsupabaseจาก./src/lib/supabase.jsและรัน query ดึงข้อมูลจาก services - รัน
node test-db.jsแล้วตรวจว่าdataแสดง Array 5 รายการ และerrorเป็นnull
รัน node test-db.js แล้ว Console แสดง Array ของบริการ 5 รายการ
และ error: null ถ่ายภาพหน้าจอ Terminal ที่แสดงผลลัพธ์จาก Supabase
หาก Error "invalid API key" ให้ตรวจสอบว่าใช้ anon public key
ไม่ใช่ service_role key (อย่าใช้ service_role key ใน Frontend หรือ Client-side code)
Key ที่ถูกต้องจะขึ้นต้นด้วย eyJ และยาวมาก