Supabase Interview — So sánh, Best Practices, Q&A
So sánh Supabase vs các giải pháp khác
Supabase vs Firebase
| Tiêu chí | Supabase | Firebase |
|---|---|---|
| Database | PostgreSQL (SQL quan hệ, full-featured) | Firestore (NoSQL document, limited query) |
| Auth | Built-in -- JWT, 50+ OAuth providers, MFA, SAML SSO | Built-in -- Proprietary, nhiều providers, MFA |
| Storage | S3-compatible, CDN toàn cầu, image transformation | Google Cloud Storage, CDN |
| Realtime | WebSocket -- Broadcast, Presence, Postgres Changes (CDC) | Firestore listeners (proprietary protocol) |
| Functions | Edge Functions (Deno, TypeScript, global edge) | Cloud Functions (Node.js, regional) |
| Open-source | Có -- toàn bộ stack | Không -- proprietary |
| Self-host | Có (Docker, Kubernetes) | Không |
| Free tier | 2 projects, 500MB DB, 50K MAU, 5GB bandwidth | 1GB Firestore, 50K reads/day, 1GB storage |
| Pricing model | Theo MAU + storage GB + compute | Theo reads/writes/deletes + bandwidth |
| Vector/Search | pgvector + hybrid search built-in | Vector Search (Firestore add-on) |
| API | REST + GraphQL auto-generated từ schema | REST + gRPC |
| Vendor lock-in | Thấp -- PostgreSQL chuẩn, dễ migrate | Cao -- lock-in Google Cloud ecosystem |
| SQL support | Đầy đủ PostgreSQL | Không (NoSQL) |
Supabase vs Neon
| Tiêu chí | Supabase | Neon |
|---|---|---|
| Database model | PostgreSQL đầy đủ (managed instance) | PostgreSQL serverless (branching-first) |
| Auth | Built-in (GoTrue) | Không |
| Storage | Có (S3-compatible) | Không |
| Realtime | Có (WebSocket CDC) | Không |
| Edge Functions | Có (Deno, global edge) | Không |
| Branching | Preview Branches | Database Branches (core feature, mạnh hơn) |
| Free tier | 2 projects, 500MB DB, 50K MAU | 0.5GB storage, 1 project |
| Scaling | Fixed compute sizes (Micro -> 16XL) | Auto-scaling (serverless) |
| Best for | Full-stack apps cần auth + DB + storage + realtime | Database-only, branching workflows, serverless apps |
Supabase vs PlanetScale
| Tiêu chí | Supabase | PlanetScale |
|---|---|---|
| Database | PostgreSQL | MySQL (Vitess-based, serverless) |
| Auth | Built-in | Không |
| Storage | Có | Không |
| Realtime | Có | Không |
| Edge Functions | Có (Deno) | Không |
| Branching | Preview Branches | Database Branches (mạnh hơn, deploy request workflow) |
| Free tier | 2 projects, 500MB | 5GB storage, 1 billion row reads/month |
| Best for | Full-stack apps | MySQL-based apps, branching workflows, high-scale reads |
So sánh tổng hợp 5 nền tảng
| Supabase | Firebase | Neon | PlanetScale | Appwrite | |
|---|---|---|---|---|---|
| Database | PostgreSQL | Firestore (NoSQL) | PostgreSQL (serverless) | MySQL (Vitess) | MariaDB |
| Auth | Built-in | Built-in | Không | Không | Built-in |
| Storage | S3 | GCS | Không | Không | Có |
| Realtime | WebSocket | Có | Không | Không | WebSocket |
| Functions | Deno Edge | Cloud Functions | Không | Không | Cloud Functions |
| Open-source | Có | Không | Không | Không | Có |
| Self-host | Có | Không | Không | Không | Có |
| GraphQL | Có (pg_graphql) | Không | Không | Không | Có |
| Vector Search | pgvector | Vector Search | pgvector | Không | Không |
| Free tier DB | 500MB | 1GB | 0.5GB | 5GB | 2GB |
Best Practices cho Supabase
Database
auth.uid() để lấy user ID từ JWT trong policies.- Dùng Supavisor transaction mode cho serverless apps (Next.js, Nuxt, Lambda) -- mỗi request là một transaction riêng, không giữ connection
- Migration-based workflow:
supabase migration new-> chỉnh sửa SQL ->supabase db push. Không sửa schema trực tiếp trên production - Type generation:
supabase gen types typescriptđể có type-safe queries -- phát hiện lỗi schema mismatch ngay khi compile - Sử dụng computed columns và database views thay vì duplicate logic ở client -- giữ business logic gần data
- Backup Storage objects riêng -- backup của Supabase chỉ cover database, không cover Storage files
- Tạo indexes cho tất cả columns dùng trong WHERE, JOIN, ORDER BY
- Dùng pg_stat_statements để phân tích slow queries
- Read Replicas cho read-heavy workloads (Pro plan add-on)
Authentication
sub (user ID), role (authenticated/anon), và email. RLS dùng auth.uid() để extract user ID từ JWT. Service role key bypass RLS -- chỉ dùng trong Edge Functions hoặc backend.- Dùng Magic Link hoặc OAuth thay vì password khi có thể -- bảo mật hơn, UX tốt hơn, không cần quản lý password
- MFA (TOTP) cho ứng dụng nhạy cảm (finance, healthcare)
- Auth Hooks để customize flow mà không cần tự host GoTrue -- gửi email tùy chỉnh, thêm claims vào JWT
onAuthStateChange()để sync auth state với UI -- handle SIGNED_IN, SIGNED_OUT, TOKEN_REFRESHED events- Custom SMTP cho transactional emails -- tránh spam filters, tăng deliverability
- CAPTCHA (Turnstile) cho auth endpoints để chống bot
Realtime
supabase_realtime. Filter server-side để giảm băng thông.- Enable RLS trên bảng subscribed để user chỉ nhận events họ được phép xem
- Dùng Broadcast cho ephemeral data (cursor positions, typing indicators) -- không cần persist
- Dùng Presence cho online status tracking -- tự động sync, tự động clean up khi disconnect
- Rate limiting: Free 2 msg/s, Pro 5 msg/s -- thiết kế app không spam quá rate limit
Edge Functions
jsr: hoặc npm: specifiers. Cold starts có thể xảy ra -- thiết kế function ngắn, idempotent. Dùng service role key trong Edge Function để bypass RLS khi cần.- Không bao giờ expose service role key ra client -- nó bypass RLS hoàn toàn
- Dùng Secrets manager (
supabase secrets set) thay vì hardcode API keys trong code - Background Tasks và Queues cho long-running jobs -- Edge Functions có timeout (dưới 10 phút)
- CORS cần được handle trong function nếu gọi từ browser
- Error handling: trả về meaningful HTTP status codes, không leak internal errors
- Logging: dùng
console.log/console.errortrong function, logs sẽ hiển thị trong Dashboard
Security
| Key | Quyền hạn | Dùng ở đâu | RLS |
|---|---|---|---|
| Publishable key (anon key) | Chỉ truy cập dữ liệu được RLS cho phép | Frontend (safe với RLS) | Tôn trọng RLS |
| JWT Secret | Ký custom JWT tokens | Backend/Edge Functions | Tùy thuộc vào JWT claims |
| Service role key | Full access, bypass RLS | Backend/Edge Functions, admin tools | Bỏ qua RLS |
- Luôn enable RLS cho tất cả public tables -- đây là lớp bảo mật quan trọng nhất
- Revoke unnecessary grants -- mặc định grants có thể quá rộng
- Dùng Column Level Security cho dữ liệu nhạy cảm (email, phone)
- CAPTCHA (Turnstile) cho auth endpoints
- Custom SMTP cho transactional emails (tránh spam filters)
- Rate limiting trên auth endpoints qua Kong gateway config
- SSL enforcement bắt buộc trên production
Performance
- Dùng connection pooling (Supavisor transaction mode) -- luôn dùng cho serverless
- Indexes: Tạo index cho columns dùng trong WHERE, JOIN, ORDER BY
- pg_stat_statements: Phân tích top slow queries
- EXPLAIN ANALYZE: Test query performance trước khi deploy
- Read Replicas: Phân tán read load (Pro plan add-on)
- Caching: Storage CDN cache cho static files, Edge Function response caching
- Compute sizing: Chọn compute phù hợp -- đừng dùng Micro cho production AI workloads
Development Workflow
# 1. Khởi tạo local project
supabase init
# 2. Start local development (cần Docker)
supabase start
# 3. Tạo migration cho thay đổi schema
supabase migration new add_users_table
# 4. Code và test local
supabase functions serve
# 5. Generate TypeScript types từ schema local
supabase gen types typescript --local > types/supabase.ts
# 6. Push lên production
supabase db push
supabase functions deploy my-function
15+ Câu hỏi phỏng vấn Supabase
Cơ bản
1. Supabase là gì? Khác Firebase ở điểm nào?
Supabase là open-source Backend-as-a-Service (BaaS) xây dựng trên nền PostgreSQL. Nó cung cấp database, authentication, storage, realtime, edge functions, và AI/vector capabilities -- tất cả tích hợp chặt chẽ với nhau.
Khác biệt cốt lõi với Firebase:
- Database: PostgreSQL (SQL quan hệ, JOINs, transactions, constraints, 70+ extensions) vs Firestore (NoSQL document, limited query, no JOINs)
- Open-source: Toàn bộ stack của Supabase là open-source, có thể self-host. Firebase hoàn toàn proprietary
- Vendor lock-in: Supabase dùng PostgreSQL chuẩn -- bạn có thể migrate sang bất kỳ Postgres provider nào. Firebase lock-in Google Cloud
- API: Supabase auto-generate REST + GraphQL từ schema. Firebase cần viết Cloud Functions hoặc dùng Firestore SDK
- Pricing: Supabase tính theo MAU + storage, Firebase tính theo operations (reads/writes/deletes)
2. RLS trong Supabase hoạt động như thế nào?
Row Level Security (RLS) là cơ chế bảo mật ở cấp hàng dữ liệu trong PostgreSQL. Nó cho phép bạn định nghĩa policies để kiểm soát user nào được đọc/ghi hàng nào.
Cách hoạt động:
- User login -> Supabase Auth tạo JWT chứa
sub(user ID) vàrole - Client gửi request kèm JWT trong
Authorizationheader - PostgREST set
auth.uid()= user ID từ JWT,auth.role()= role từ JWT - PostgreSQL kiểm tra RLS policies trước khi thực thi query
- Nếu policy không cho phép -> row bị lọc khỏi kết quả (SELECT) hoặc query bị reject (INSERT/UPDATE/DELETE)
-- Ví dụ: user chỉ xem được post của chính mình
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
-- Policy cho insert: user chỉ tạo post với user_id của chính mình
CREATE POLICY "Users can create own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
3. Làm thế nào để bảo vệ API key khi gọi OpenAI từ frontend?
Không bao giờ gọi OpenAI trực tiếp từ frontend. API key sẽ bị expose trong network tab.
Giải pháp đúng: Dùng Edge Function làm proxy:
// supabase/functions/chat/index.ts
import { createClient } from 'jsr:@supabase/supabase-js@2'
const OPENAI_API_KEY = Deno.env.get('OPENAI_API_KEY')! // Secret, không expose
Deno.serve(async (req: Request) => {
const { message } = await req.json()
// Verify user authentication
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
const token = req.headers.get('Authorization')?.replace('Bearer ', '')
const { data: { user }, error } = await supabase.auth.getUser(token)
if (error || !user) {
return new Response('Unauthorized', { status: 401 })
}
// Gọi OpenAI với API key từ secrets
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${OPENAI_API_KEY}`
},
body: JSON.stringify({
model: 'gpt-4o',
messages: [{ role: 'user', content: message }]
})
})
const data = await response.json()
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
})
})
4. Postgres Changes (CDC) hoạt động thế nào?
Postgres Changes dùng logical replication của PostgreSQL (WAL -- Write-Ahead Log):
- Khi có INSERT/UPDATE/DELETE, PostgreSQL ghi thay đổi vào WAL
- Publication
supabase_realtimelắng nghe WAL cho các bảng được đăng ký - Supabase Realtime server đọc WAL records và broadcast qua WebSocket đến subscribed clients
- Client nhận payload với
eventType,new(row mới),old(row cũ -- cho UPDATE/DELETE)
-- Đăng ký bảng vào realtime publication
ALTER PUBLICATION supabase_realtime ADD TABLE messages;
// Client subscribe
supabase.channel('db-changes')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => console.log('New message:', payload.new)
)
.subscribe()
filter: 'room_id=eq.123'), giúp giảm băng thông -- client chỉ nhận events phù hợp. Kết hợp với RLS để đảm bảo user chỉ nhận events từ dữ liệu họ được phép xem.5. Khi nào dùng Broadcast vs Presence vs Postgres Changes?
| Chế độ | Dùng khi | Dữ liệu | Ví dụ |
|---|---|---|---|
| Postgres Changes | Cần sync dữ liệu đã persist vào database | Persistent, authoritative | Live feed, dashboard, collaborative docs |
| Broadcast | Cần gửi message realtime không cần persist | Ephemeral, low-latency | Chat messages, cursor tracking, game events |
| Presence | Cần theo dõi trạng thái online của user | Ephemeral, automatically cleaned up | Online users list, "ai đang typing", active participants |
Nguyên tắc chọn:
- Nếu dữ liệu cần lưu lại -> Postgres Changes
- Nếu dữ liệu là tạm thời, cần low latency -> Broadcast
- Nếu cần biết ai đang online -> Presence
Trung cấp
6. Supabase Auth flow: sign up -> email verify -> JWT -> RLS
Toàn bộ flow auth trong Supabase:
- Sign Up: User gửi email + password -> GoTrue tạo user trong
auth.users(status:unconfirmed) - Email Verify: GoTrue gửi email xác nhận (qua SMTP của bạn hoặc Supabase default) -> User click link -> status chuyển thành
confirmed - Sign In: User gửi email + password -> GoTrue verify -> Tạo JWT với claims:
sub(user UUID),role(authenticated),email,aud - Client lưu JWT: localStorage (web) hoặc secure storage (mobile). Supabase client tự động attach JWT vào mọi request
- API Request: Client gọi
supabase.from('posts').select()-> JWT gửi trongAuthorization: Bearer [JWT] - PostgREST: Set
auth.uid()=subtừ JWT,auth.role()=roletừ JWT - RLS Check: PostgreSQL check policies dùng
auth.uid()-> chỉ trả về rows user được phép xem
7. Supavisor session mode vs transaction mode?
| Session Mode | Transaction Mode | |
|---|---|---|
| Connection | Giữ connection suốt session | Mỗi transaction lấy connection mới |
| Prepared Statements | Hỗ trợ | Không hỗ trợ |
| Advisory Locks | Hỗ trợ | Không hỗ trợ |
| SET statements | Persist trong session | Reset sau mỗi transaction |
| Serverless apps | Có thể cạn pool | Tối ưu |
| Port | 6543 | 5432 |
Khuyến nghị:
- Serverless (Next.js, Nuxt, Lambda): Dùng transaction mode -- mỗi request là một transaction, pool hiệu quả hơn
- Long-running apps (Express, traditional servers): Dùng session mode nếu cần prepared statements hoặc advisory locks
- Direct connection (port 5432): Cho ORMs, database tools, migrations
8. Làm sao migrate từ Firebase sang Supabase?
Quy trình migration Firebase -> Supabase gồm 3 phần:
1. Auth Migration:
# Export Firebase users (bao gồm password hash parameters)
node firestoreusers2json.js users.json 100
# Import vào Supabase Auth (giữ nguyên password hash)
node import_users.js users.json 100
- Giữ Firebase password hash parameters (scrypt config) để user không cần reset password
- Map Firebase UID sang Supabase
auth.users.id
2. Firestore Migration:
- Export Firestore collections ra JSON
- Thiết kế lại schema -- "flatten" nested documents thành relational tables với foreign keys
- Import vào Postgres bằng script
json2supabase.js - Ví dụ:
posts/{postId}/comments/{commentId}-> bảngpostsvà bảngcommentsvớipost_idFK
3. Storage Migration:
- Download files từ Firebase Storage -> Upload lên Supabase Storage
- Map metadata và access controls sang RLS policies
9. Edge Functions vs traditional backend?
| Edge Functions (Supabase) | Traditional Backend (Express, FastAPI) | |
|---|---|---|
| Runtime | Deno (TypeScript native) | Node.js, Python, Go, etc. |
| Deployment | Global edge (gần user) | Regional server |
| Cold start | Có (vài ms đến vài trăm ms) | Không (luôn warm nếu dùng server) |
| Timeout | Giới hạn (vài phút) | Không giới hạn thực tế |
| State | Stateless | Có thể stateful |
| Scaling | Tự động | Manual hoặc auto-scaling config |
| Best for | Webhooks, API proxies, lightweight logic | Complex business logic, long-running tasks, stateful apps |
| Database access | Qua supabase-js với service role | Direct connection hoặc ORM |
| Cost | Tính theo invocation | Tính theo server uptime |
Quy tắc chọn:
- Edge Functions: webhook receivers, API key proxying (OpenAI, Stripe), lightweight transforms, sending emails
- Traditional backend: complex workflows, long-running jobs (video processing), stateful sessions, legacy integrations
10. pgvector trong Supabase dùng cho gì?
pgvector là PostgreSQL extension cho phép lưu và query vector embeddings ngay trong database:
Use cases chính:
- Semantic Search: Tìm kiếm theo ý nghĩa, không phải keyword chính xác (FAQ, documentation)
- RAG (Retrieval Augmented Generation): Cung cấp context cho LLM từ knowledge base
- Recommendations: Gợi ý sản phẩm/bài viết tương tự dựa trên embeddings
- Image/Video Search: Tìm kiếm đa phương tiện bằng vector similarity
- Anomaly Detection: Phát hiện bất thường bằng distance metrics
-- Tạo bảng với vector column
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT,
embedding VECTOR(1536) -- OpenAI ada-002 embedding dimension
);
-- Tạo HNSW index cho fast approximate search
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
-- Similarity search
SELECT content, 1 - (embedding <=> query_embedding) AS similarity
FROM documents
ORDER BY embedding <=> query_embedding
LIMIT 5;
Nâng cao
11. Tại sao nên dùng Supabase thay vì tự build backend?
Supabase phù hợp khi:
- Team nhỏ (1-5 developers), muốn ship nhanh -- không có thời gian build auth, storage, realtime từ đầu
- MVP/POC: Cần validate idea trong vài ngày/tuần -- Supabase cho phép có full backend trong vài giờ
- PostgreSQL là lựa chọn đúng: Bạn cần SQL quan hệ, không phải NoSQL
- Cần auth + database tích hợp chặt: RLS + JWT = authorization tự động không cần viết middleware
- Dự án open-source hoặc muốn tránh vendor lock-in
- Cần realtime capabilities: Postgres Changes, Broadcast, Presence có sẵn
Không nên dùng Supabase khi:
- Cần kiểm soát hoàn toàn infrastructure và kiến trúc
- Có team backend mạnh, đã có sẵn boilerplate/auth system
- Yêu cầu compliance đặc thù không được Supabase hỗ trợ
- High-frequency trading hoặc systems cần microsecond latency
12. Storage trong Supabase có gì đặc biệt?
- S3-compatible API: Tương thích với mọi S3 client library
- RLS tích hợp: Cùng cơ chế bảo mật với database --
storage.objectspolicies - CDN toàn cầu: Phục vụ file từ 285+ thành phố
- Image Transformation: Resize, compress, format conversion qua URL parameters (không cần xử lý server-side)
- TUS Resumable Uploads: Upload file lớn với pause/resume
- Signed URLs: Temporary access links với expiration
- Metadata trong Postgres: Mọi file metadata được lưu trong bảng
storage.objects, có thể JOIN với data khác
13. Supabase có scale được không?
Có. Supabase scale theo nhiều chiều:
- Vertical scaling: Compute add-ons từ Micro (2-core, 1GB) đến 16XL (64-core, 256GB)
- Horizontal scaling (reads): Read Replicas trên Pro plan
- Connection pooling: Supavisor quản lý hàng nghìn connections hiệu quả
- Edge Functions: Tự động scale theo request volume trên edge network toàn cầu
- Storage CDN: File được cache và phục vụ từ edge gần user nhất
- Disk: General Purpose lên đến 16TB, High Performance lên đến 80,000 IOPS
Giới hạn cần lưu ý:
- PostgreSQL là single-writer -- write throughput có giới hạn
- Realtime connections: Free 200, Pro 500 concurrent (có thể tăng)
- Edge Functions timeout (vài phút) -- không phù hợp cho long-running jobs
14. Self-host vs Managed -- pros/cons?
| Managed Supabase | Self-Hosted | |
|---|---|---|
| Setup time | Vài phút | Vài giờ đến vài ngày |
| Maintenance | Supabase lo | Bạn lo |
| Backups | Tự động daily + PITR | Tự cấu hình |
| Scaling | Click upgrade compute | Tự provision server |
| Updates | Tự động | Tự cập nhật |
| Cost | $0-$25/tháng (start) | Server cost + thời gian vận hành |
| Data control | Trên cloud của Supabase | Trên infrastructure của bạn |
| Compliance | SOC2, HIPAA (add-on) | Tự đáp ứng |
| Missing features | Đầy đủ | Không có Branching, Analytics Buckets, Vector Buckets, Log Drains, Pipelines |
15. Khi nào KHÔNG nên dùng Supabase?
- Bạn cần NoSQL: Dữ liệu unstructured, schema thay đổi liên tục, không cần JOINs/transactions -> Firebase hoặc MongoDB phù hợp hơn
- High-frequency trading / Gaming server: Cần microsecond latency, in-memory state -> Dùng Redis, custom C++/Rust server
- Đã có team backend mạnh + hệ thống auth/storage sẵn: Không cần BaaS -> chỉ dùng PostgreSQL managed service (RDS, Neon)
- Yêu cầu compliance đặc thù: On-premise only, air-gapped networks, FIPS 140-2 -> Cần enterprise solutions
- Cần push notifications (FCM/APNs): Supabase không có push notification service -> Cần Firebase Cloud Messaging hoặc third-party
- Time-series data ở scale lớn: PostgreSQL có thể handle, nhưng TimescaleDB hoặc InfluxDB tối ưu hơn
- Cần full-text search engine cấp enterprise: PostgreSQL full-text search tốt nhưng không bằng Elasticsearch/Algolia cho scale rất lớn
- Budget siêu hạn chế + traffic cao: Firebase pricing có thể rẻ hơn cho app có nhiều reads nhưng ít users do tính theo operations