Vibe CodingSupabaseDatabaseCursorBackend

Vibe Coding กับ Database — สอนใช้ Cursor + Supabase

เรียน Vibe Coding กับ Supabase — ตั้ง database, สร้าง schema, RLS, TypeScript types ด้วย Cursor ใน 30 นาที เริ่มสร้าง app จริงได้วันนี้!

AI Unlocked Team
23/06/2569

Vibe Coding กับ Database — สอนใช้ Cursor + Supabase

หนึ่งในคำถามที่ได้ยินบ่อยที่สุดจากคนที่เริ่มต้น Vibe Coding คือ — "แล้วถ้าอยากให้ app เก็บข้อมูลได้จริงๆ ต้องทำยังไง?"

Frontend สวยได้ด้วย Cursor + Claude แต่ถ้าไม่มี database ข้อมูลก็หายไปทุกครั้งที่รีเฟรชหน้า ปัญหานี้แก้ได้ง่ายมากด้วย Supabase — backend ที่ออกแบบมาให้นักพัฒนา (และ Vibe Coder) ใช้งานได้เร็วที่สุดในโลก

บทความนี้จะพาคุณจาก "ไม่มี database" ไปถึง "มี CRUD app ที่ทำงานได้จริง" ภายใน 30 นาที โดยใช้ Cursor เป็นตัวช่วยเขียนโค้ด

ถ้ายังไม่รู้จัก Vibe Coding เลย แนะนำให้อ่าน Vibe Coding คืออะไร — คู่มือฉบับสมบูรณ์ ก่อน


1. ทำไม Supabase ถึงเป็น Backend ที่ดีที่สุดสำหรับ Vibe Coding

เวลาเริ่มโปรเจกต์ใหม่ด้วย Vibe Coding คุณมีตัวเลือก backend หลายอย่าง — Firebase, PlanetScale, Neon, Railway แต่ละอันมีข้อดีต่างกัน แต่ Supabase มีสิ่งที่ทำให้มันโดดเด่นกว่าสำหรับงาน Vibe Coding โดยเฉพาะ

เหตุผลที่ 1: Cursor รู้จัก Supabase API ดีมาก

เนื่องจาก Supabase เป็น open-source และมี documentation สาธารณะจำนวนมาก Claude (ที่อยู่ใน Cursor) จึงรู้ pattern การใช้งานเป็นอย่างดี พิมพ์ว่า "สร้าง function ดึงข้อมูล users จาก Supabase" แล้ว Cursor จะเขียน code ที่ถูกต้องได้ทันที ไม่ต้องแก้ไขเพิ่มเติมมาก

เหตุผลที่ 2: JavaScript SDK ใช้งานง่ายมาก

Supabase JS SDK ถูกออกแบบให้ readable สูง ทำให้ Vibe Coder อ่านแล้วเข้าใจได้ว่า code ทำอะไร แม้จะไม่มีพื้นฐาน programming

เหตุผลที่ 3: Free tier ใจกว้าง

Project ฟรีได้ถึง 500MB database, 2GB bandwidth, unlimited API requests สำหรับ prototype หรือ side project ไม่ต้องจ่ายเงินเลย

เหตุผลที่ 4: ครบในที่เดียว

Database + Authentication + File Storage + Realtime + Edge Functions — ทุกอย่างมาพร้อมกัน ไม่ต้องต่อ service แยก 5 อัน


2. Supabase คืออะไร — รู้ก่อนสร้าง

Supabase คือ open-source Firebase alternative ที่ใช้ PostgreSQL เป็น database หลัก

ประกอบด้วย 4 ส่วนหลัก:

PostgreSQL Database

Database หลักที่เก็บข้อมูลทั้งหมด เป็น PostgreSQL จริงๆ ใช้ SQL ได้เต็มรูปแบบ ต้องการ query ซับซ้อนแค่ไหนก็ได้ รองรับ JSON fields, full-text search, geographic data

Authentication

ระบบ auth สำเร็จรูป รองรับ Email/Password, Magic Link, OAuth (Google, GitHub, Facebook) ฯลฯ ไม่ต้องเขียน auth logic เอง Cursor จัดการได้หมด

Storage

File storage สำหรับ images, videos, documents สร้าง bucket, ตั้ง permission ได้ผ่าน dashboard มี CDN ในตัว

Realtime

Subscribe ดู database changes แบบ real-time ใช้ WebSocket เหมาะสำหรับ chat app, live dashboard, collaborative tools


3. Setup Project แรกใน 5 นาที

เริ่มจากสร้าง Supabase project ก่อน ทำตามขั้นตอนนี้:

ขั้นตอนที่ 1: สร้าง Account และ Project

  1. ไปที่ supabase.com แล้ว Sign Up ด้วย GitHub account
  2. กด New project
  3. ตั้งชื่อ project (เช่น my-vibe-app)
  4. ตั้ง database password (จดไว้ — ใช้อีก)
  5. เลือก region ที่ใกล้ที่สุด (Singapore สำหรับคนไทย)
  6. กด Create new project รอประมาณ 2 นาที

ขั้นตอนที่ 2: ดึง API Keys

หลัง project สร้างเสร็จ ไปที่ Settings → API แล้วก็อป:

  • Project URL — เป็น URL ที่ใช้เชื่อมต่อ
  • anon/public key — ใช้ใน frontend code

ขั้นตอนที่ 3: ติดตั้ง Supabase ใน Next.js Project

ใน terminal ของ Cursor ให้รัน:

npm install @supabase/supabase-js

สร้างไฟล์ .env.local ใน root ของ project:

NEXT_PUBLIC_SUPABASE_URL=your_project_url_here
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key_here

สร้างไฟล์ lib/supabase.ts:

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient(supabaseUrl, supabaseAnonKey)

เพียงเท่านี้ backend ก็พร้อมใช้งานแล้ว


4. ขอให้ Cursor สร้าง Schema + RLS Policies

นี่คือส่วนที่ Vibe Coding เจ๋งมาก แทนที่จะต้องนั่งเขียน SQL เอง ให้ Cursor ทำให้

วิธี Prompt ใน Cursor

เปิด Cursor แล้วพิมพ์ใน chat:

"ฉันกำลังสร้าง task management app ด้วย Supabase PostgreSQL ช่วยสร้าง SQL schema สำหรับตาราง tasks ที่มี: id, title, description, status (todo/in-progress/done), created_at, updated_at, user_id (foreign key ไปยัง auth.users) พร้อม RLS policies ที่ให้ user เห็นและแก้ไขได้เฉพาะ tasks ของตัวเอง"

Cursor จะ generate SQL ออกมาแบบนี้:

-- สร้างตาราง tasks
CREATE TABLE tasks (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  title TEXT NOT NULL,
  description TEXT,
  status TEXT DEFAULT 'todo' CHECK (status IN ('todo', 'in-progress', 'done')),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL
);

-- เปิด Row Level Security
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

-- Policy: user เห็นได้เฉพาะ tasks ของตัวเอง
CREATE POLICY "Users can view own tasks" ON tasks
  FOR SELECT USING (auth.uid() = user_id);

-- Policy: user สร้าง task ให้ตัวเองได้
CREATE POLICY "Users can insert own tasks" ON tasks
  FOR INSERT WITH CHECK (auth.uid() = user_id);

-- Policy: user แก้ไข tasks ของตัวเองได้
CREATE POLICY "Users can update own tasks" ON tasks
  FOR UPDATE USING (auth.uid() = user_id);

-- Policy: user ลบ tasks ของตัวเองได้
CREATE POLICY "Users can delete own tasks" ON tasks
  FOR DELETE USING (auth.uid() = user_id);

-- Auto-update updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ language 'plpgsql';

CREATE TRIGGER update_tasks_updated_at
  BEFORE UPDATE ON tasks
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

วิธี Run SQL ใน Supabase

ไปที่ Supabase Dashboard → SQL Editor แล้ว paste SQL ด้านบนแล้วกด Run

RLS (Row Level Security) สำคัญมาก มันป้องกันไม่ให้ user คนหนึ่งเห็นข้อมูลของ user อื่น ถือเป็น best practice สำหรับทุก Supabase project


5. Generate TypeScript Types อัตโนมัติ

Supabase มี CLI ที่สามารถ generate TypeScript types จาก database schema ได้อัตโนมัติ ทำให้ Cursor ช่วยเขียน code ได้แม่นยำขึ้นมาก

ติดตั้ง Supabase CLI

npm install -g supabase

Login และ Generate Types

# Login ด้วย access token จาก Supabase Dashboard → Settings → Access Tokens
supabase login

# Generate types (แทนที่ <project-id> ด้วย Project ID ของคุณ)
supabase gen types typescript --project-id <project-id> > lib/database.types.ts

อัปเดต Supabase Client ให้ใช้ Types

import { createClient } from '@supabase/supabase-js'
import type { Database } from './database.types'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)

หลังจากนี้ เวลาพิมพ์ supabase.from('tasks') ใน Cursor จะมี autocomplete บอก columns ทั้งหมดที่มีในตาราง ลด error จากการพิมพ์ชื่อ column ผิดได้เยอะมาก


6. ตัวอย่าง: สร้าง CRUD App ใน 30 นาที

มาลงมือสร้าง Task Manager app จริงๆ กัน โดยใช้ Cursor ช่วย

โครงสร้าง Function ที่ต้องมี

บอก Cursor ว่าต้องการอะไร:

"ช่วยสร้าง custom hook useTasks สำหรับ Next.js ที่ใช้ Supabase เพื่อ: ดึง tasks ทั้งหมดของ user ที่ login อยู่, สร้าง task ใหม่, อัปเดต status ของ task, ลบ task"

Cursor จะเขียน code ออกมาประมาณนี้:

// hooks/useTasks.ts
import { useState, useEffect } from 'react'
import { supabase } from '@/lib/supabase'

type Task = {
  id: string
  title: string
  description: string | null
  status: 'todo' | 'in-progress' | 'done'
  created_at: string
  updated_at: string
  user_id: string
}

export function useTasks() {
  const [tasks, setTasks] = useState<Task[]>([])
  const [loading, setLoading] = useState(true)

  // ดึง tasks ทั้งหมด
  const fetchTasks = async () => {
    setLoading(true)
    const { data, error } = await supabase
      .from('tasks')
      .select('*')
      .order('created_at', { ascending: false })

    if (!error && data) {
      setTasks(data)
    }
    setLoading(false)
  }

  // สร้าง task ใหม่
  const createTask = async (title: string, description?: string) => {
    const { data, error } = await supabase
      .from('tasks')
      .insert({ title, description })
      .select()
      .single()

    if (!error && data) {
      setTasks(prev => [data, ...prev])
    }
    return { data, error }
  }

  // อัปเดต status
  const updateTaskStatus = async (id: string, status: Task['status']) => {
    const { error } = await supabase
      .from('tasks')
      .update({ status })
      .eq('id', id)

    if (!error) {
      setTasks(prev =>
        prev.map(task => task.id === id ? { ...task, status } : task)
      )
    }
    return { error }
  }

  // ลบ task
  const deleteTask = async (id: string) => {
    const { error } = await supabase
      .from('tasks')
      .delete()
      .eq('id', id)

    if (!error) {
      setTasks(prev => prev.filter(task => task.id !== id))
    }
    return { error }
  }

  useEffect(() => {
    fetchTasks()
  }, [])

  return { tasks, loading, createTask, updateTaskStatus, deleteTask }
}

หน้า UI หลัก

"ช่วยสร้าง TaskList component ที่ใช้ hook useTasks แสดง tasks เป็น list มีปุ่ม เปลี่ยน status และลบ task แต่ละอัน รวมถึงฟอร์มสร้าง task ใหม่ ออกแบบ UI ด้วย Tailwind CSS ให้ดูสะอาดและ modern"

ใน 5 นาทีคุณจะมี component ที่ทำงานได้จริง — ไม่ต้องเขียนโค้ดเองสักบรรทัด

สำหรับการ deploy ขั้นตอนถัดไปอ่านได้ที่ วิธี Deploy Next.js App ขึ้น Vercel ด้วย Vibe Coding


7. Tips ขั้นสูง: Migration, Edge Functions, Auth Flow

Database Migration

เมื่อต้องการเปลี่ยน schema ในภายหลัง อย่าแก้โดยตรงใน dashboard ให้บอก Cursor:

"ฉันต้องการเพิ่ม column priority (low/medium/high) ใน tasks table และ column due_date แบบ optional ช่วยเขียน SQL migration ที่ปลอดภัย ไม่ทำลายข้อมูลเดิม"

Cursor จะเขียน ALTER TABLE แบบที่ safe กว่าการลบ table แล้วสร้างใหม่

ALTER TABLE tasks
  ADD COLUMN priority TEXT DEFAULT 'medium'
    CHECK (priority IN ('low', 'medium', 'high')),
  ADD COLUMN due_date TIMESTAMPTZ;

Edge Functions

สำหรับ business logic ที่ซับซ้อนหรือต้องการ server-side processing ใช้ Supabase Edge Functions:

"ช่วยสร้าง Supabase Edge Function ที่ส่ง email notification เมื่อ task ถูกสร้างใหม่ โดยใช้ Resend API"

Edge Functions คือ Deno functions ที่รันบน Supabase servers เหมาะสำหรับ webhook, cron jobs, หรือ operations ที่ต้องการ secret keys

Auth Flow

Supabase Auth กับ Next.js ตั้งค่าได้ด้วย @supabase/ssr package:

"ช่วย setup Supabase Auth ใน Next.js 14 App Router ให้มี middleware ป้องกัน routes ที่ต้อง login และมีหน้า login/logout"

Cursor จะสร้าง middleware.ts, auth helper functions และ login page ให้ครบ

สำหรับ setup Cursor ที่สมบูรณ์ อ่านได้ที่ Cursor + Claude Vibe Coding Setup ฉบับสมบูรณ์


8. Common Pitfalls + วิธีแก้

ข้อผิดพลาดที่พบบ่อยเมื่อเริ่มใช้ Supabase กับ Vibe Coding และวิธีแก้:

Pitfall 1: RLS ทำให้ดึงข้อมูลไม่ได้

อาการ: Query คืนค่า empty array ทั้งที่มีข้อมูลอยู่

สาเหตุ: เปิด RLS แต่ยังไม่ได้สร้าง policies หรือ user ยังไม่ได้ login

วิธีแก้: ตรวจสอบใน Supabase Dashboard → Authentication → Policies ว่ามี policy สำหรับ SELECT หรือเปล่า และตรวจสอบว่า user session ถูกต้อง

// เช็ค session ก่อน query
const { data: { session } } = await supabase.auth.getSession()
if (!session) {
  // redirect ไปหน้า login
  return
}

Pitfall 2: Environment Variables ใช้งานไม่ได้ใน Production

อาการ: App ทำงานใน local แต่ใน Vercel ดึงข้อมูลไม่ได้

สาเหตุ: ลืม set environment variables ใน Vercel dashboard

วิธีแก้: ไปที่ Vercel → Project Settings → Environment Variables แล้ว add NEXT_PUBLIC_SUPABASE_URL และ NEXT_PUBLIC_SUPABASE_ANON_KEY

Pitfall 3: CORS Error เวลา Upload ไฟล์

อาการ: Upload file ไปยัง Supabase Storage แล้วเจอ CORS error

วิธีแก้: ไปที่ Supabase Dashboard → Storage → Policies แล้วเพิ่ม CORS policy ให้ถูกต้อง หรือบอก Cursor:

"ฉันเจอ CORS error เวลา upload ไฟล์ไปยัง Supabase Storage จาก localhost:3000 ช่วยแก้ไขให้หน่อย"

Pitfall 4: N+1 Query Problem

อาการ: App ช้าผิดปกติเมื่อมีข้อมูลเยอะ

สาเหตุ: ดึงข้อมูล list แล้วค่อย loop ดึง related data ทีละรายการ

วิธีแก้: ใช้ Supabase's relationship query แทน:

// ไม่ดี — ดึงทีละ user
const tasks = await supabase.from('tasks').select('*')
for (const task of tasks.data) {
  const user = await supabase.from('profiles').select('*').eq('id', task.user_id)
}

// ดี — ดึงพร้อมกันครั้งเดียว
const { data } = await supabase
  .from('tasks')
  .select('*, profiles(name, avatar_url)')

Pitfall 5: Forgetting to Handle Errors

อาการ: App crash โดยไม่มี error message ที่ชัดเจน

วิธีแก้: เสมอ destructure error จาก Supabase response:

const { data, error } = await supabase.from('tasks').select('*')

if (error) {
  console.error('Error fetching tasks:', error.message)
  // แจ้ง user ด้วย toast หรือ error state
  return
}

บอก Cursor ว่า "เพิ่ม error handling ที่ครอบคลุมใน useTasks hook" แล้วมันจะเพิ่มให้อัตโนมัติ


Vibe Coding + Supabase = Stack ที่ทรงพลังที่สุดสำหรับมือใหม่

สรุปสิ่งที่คุณทำได้ตอนนี้:

  • ตั้ง Supabase project ได้ใน 5 นาที
  • ขอให้ Cursor เขียน SQL schema + RLS policies ให้
  • Generate TypeScript types อัตโนมัติ
  • สร้าง CRUD operations โดยใช้ Supabase JavaScript SDK
  • หลีกเลี่ยง pitfalls ที่พบบ่อย

Supabase กับ Cursor เป็น combo ที่ทำให้ Vibe Coding ไปได้ไกลกว่าแค่ static website มาก คุณสามารถสร้าง app ที่มี user authentication, เก็บข้อมูล, และ scale ได้จริงโดยไม่ต้องเป็น backend developer

สำหรับ best practices เพิ่มเติมในการ Vibe Code ให้ได้คุณภาพ อ่านได้ที่ Vibe Coding Best Practices — เขียน AI Code ให้ดีและปลอดภัย


ก้าวต่อไป

ถ้าอยากเรียน Vibe Coding แบบมีโครงสร้างและมีอาจารย์คอยแนะนำ:

คำถามหรือข้อสงสัยเพิ่มเติม ทักมาได้ที่ AI Unlocked Community เลย

เขียนโดย

AI Unlocked Team