Skip to content

Chương 3: Supabase — Cloud Database Cho Team

"Đức nói: 'SQLite là đủ cho prototype.' Lan phản bác: 'Team 5 người cùng dev, cần cloud database. Supabase free tier đủ mạnh.' Tuấn đã setup xong trong 10 phút."


🎯 Mục tiêu

  • Tạo Supabase project & hiểu kiến trúc
  • Thiết kế schema cho TeamFlow (PostgreSQL)
  • Setup Supabase Auth, Storage, Realtime
  • Kết nối từ Express app

Phần 1: Tại Sao Supabase? (15 phút)

So Sánh Database Options

FeatureSQLiteFirebaseSupabase ✅
TypeFile-basedNoSQLPostgreSQL
Team access❌ Local only✅ Cloud✅ Cloud
Auth built-in
Storage
Realtime
SQL
Free tierN/ALimited500MB DB, 1GB Storage
Open sourceN/A

Supabase Architecture

┌──────────────────────────────────────┐
│          Supabase Platform           │
├──────────┬───────────┬──────────────┤
│ Database │ Auth      │ Storage      │
│(Postgres)│(JWT/OAuth)│(S3-like)     │
├──────────┼───────────┼──────────────┤
│ Realtime │ Edge Fn   │ REST API     │
│(WebSocket│(Deno)     │(PostgREST)   │
└──────────┴───────────┴──────────────┘
     ↑ All accessible via supabase-js client

Phần 2: Setup Supabase Project (25 phút)

Bước 1: Tạo Project

  1. Vào supabase.com → New Project
  2. Đặt tên: teamflow
  3. Database password: (ghi nhớ!)
  4. Region: Southeast Asia (Singapore)
  5. Chờ 2 phút setup

Bước 2: Lấy Credentials

bash
# Từ Supabase Dashboard → Settings → API
# Copy 2 giá trị:

SUPABASE_URL=https://xxxxx.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIs...
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIs...  # Server-side only!

Bước 3: Setup trong Project

bash
# Trong teamflow-app/
npm install @supabase/supabase-js

# Tạo .env
cat > .env << EOF
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_KEY=your-service-key
PORT=3000
EOF

Bước 4: Supabase Client

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

const supabaseUrl = process.env.SUPABASE_URL!
const supabaseKey = process.env.SUPABASE_SERVICE_KEY!

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

Phần 3: Schema Design (30 phút)

TeamFlow Database Schema

sql
-- Supabase SQL Editor → New Query

-- Users (extends Supabase auth.users)
CREATE TABLE profiles (
  id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  name TEXT NOT NULL CHECK (char_length(name) BETWEEN 1 AND 100),
  role TEXT NOT NULL DEFAULT 'dev' CHECK (role IN ('admin','dev','qa','pm')),
  avatar_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Projects
CREATE TABLE projects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL CHECK (char_length(name) BETWEEN 1 AND 200),
  description TEXT,
  color TEXT DEFAULT '#6366f1',
  owner_id UUID NOT NULL REFERENCES profiles(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tasks
CREATE TABLE tasks (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title TEXT NOT NULL CHECK (char_length(title) BETWEEN 1 AND 500),
  description TEXT,
  status TEXT NOT NULL DEFAULT 'todo'
    CHECK (status IN ('todo','in_progress','review','done')),
  priority TEXT NOT NULL DEFAULT 'medium'
    CHECK (priority IN ('low','medium','high','urgent')),
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  assignee_id UUID REFERENCES profiles(id),
  due_date TIMESTAMPTZ,
  position INTEGER DEFAULT 0,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Sprints
CREATE TABLE sprints (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
  start_date TIMESTAMPTZ NOT NULL,
  end_date TIMESTAMPTZ NOT NULL,
  status TEXT DEFAULT 'planned' CHECK (status IN ('planned','active','completed')),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Labels
CREATE TABLE labels (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  color TEXT NOT NULL DEFAULT '#6366f1',
  project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE
);

-- Task-Label junction
CREATE TABLE task_labels (
  task_id UUID REFERENCES tasks(id) ON DELETE CASCADE,
  label_id UUID REFERENCES labels(id) ON DELETE CASCADE,
  PRIMARY KEY (task_id, label_id)
);

-- Row Level Security
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE tasks ENABLE ROW LEVEL SECURITY;

-- Policies
CREATE POLICY "Users can view all profiles"
  ON profiles FOR SELECT USING (true);

CREATE POLICY "Users can update own profile"
  ON profiles FOR UPDATE USING (auth.uid() = id);

CREATE POLICY "Authenticated users can view projects"
  ON projects FOR SELECT USING (auth.role() = 'authenticated');

CREATE POLICY "Owners can manage projects"
  ON projects FOR ALL USING (auth.uid() = owner_id);

CREATE POLICY "Team can view tasks"
  ON tasks FOR SELECT USING (auth.role() = 'authenticated');

CREATE POLICY "Team can manage tasks"
  ON tasks FOR ALL USING (auth.role() = 'authenticated');

Lab: Run Schema

bash
antigravity "Connect to my Supabase project and verify the schema:
1. Check that profiles, projects, tasks, sprints, labels tables exist
2. Verify RLS policies are active
3. List all tables with column counts"

Phần 4: Seed Data (15 phút)

sql
-- Insert Team Phoenix members (after they register via Auth)
-- This runs as migration seed

INSERT INTO profiles (id, name, role) VALUES
  ('00000000-0000-0000-0000-000000000001', 'Minh (CEO)', 'admin'),
  ('00000000-0000-0000-0000-000000000002', 'Lan (PM)', 'pm'),
  ('00000000-0000-0000-0000-000000000003', 'Đức (Sr Dev)', 'dev'),
  ('00000000-0000-0000-0000-000000000004', 'Hà (QA)', 'qa'),
  ('00000000-0000-0000-0000-000000000005', 'Tuấn (Jr Dev)', 'dev');

INSERT INTO projects (name, description, color, owner_id) VALUES
  ('TeamFlow MVP', 'AI-Native Project Management Platform', '#6366f1',
   '00000000-0000-0000-0000-000000000002');

Phần 5: Supabase Auth Preview (10 phút)

typescript
// Preview — will implement fully in Chapter 6
import { supabase } from './lib/supabase'

// Sign up
const { data, error } = await supabase.auth.signUp({
  email: 'duc@phoenix.tech',
  password: 'securePass123'
})

// Sign in
const { data: session } = await supabase.auth.signInWithPassword({
  email: 'duc@phoenix.tech',
  password: 'securePass123'
})

// Get user
const { data: { user } } = await supabase.auth.getUser()

Quiz

Q1: Supabase dùng database engine gì?

  • A) MongoDB
  • B) PostgreSQL ✅
  • C) MySQL

Q2: RLS (Row Level Security) giúp gì?

  • A) Mã hóa data
  • B) Kiểm soát ai xem/sửa data ở database level ✅
  • C) Backup tự động

Homework

  • [ ] Tạo Supabase project
  • [ ] Run schema SQL
  • [ ] Insert seed data
  • [ ] Test query trong SQL Editor

Chương tiếp: Dự Án TeamFlow — Kiến Trúc & Khởi Tạo →

Powered by CodyMaster × VitePress