# Supabase cho FDE: dựng backend có đăng nhập, Postgres và Edge Functions cho demo trong một buổi

> Dựng backend cho khách trong một buổi chiều không khó. Cái khó là để mỗi người dùng chỉ thấy đúng phần dữ liệu của mình trước khi khách bấm thử.

Bản gốc: https://fdetimes.net/vi/cong-cu/supabase-backend-nhanh-cho-fde-auth-postgres-edge-functions/

Chỉ cần thiếu một dòng SQL là bản demo của bạn để lộ dữ liệu của mọi người dùng. Hoặc nó hiện ra một màn hình trống đúng lúc khách đăng nhập thử. Với Supabase, cả hai kết cục đều bắt nguồn từ cùng một chỗ: Row Level Security.

Vì thế, bài này xem Supabase là công cụ làm việc của FDE chứ không chỉ là tiện ích cho side project. Khi khách hỏi "chiều nay cho xem được không?", bạn cần một backend có đăng nhập, có database thật và chạy được một ít logic phía server. Bạn cũng phải chắc rằng người dùng A không nhìn thấy dữ liệu của người dùng B.

## Vì sao một buổi là đủ?

Supabase tự giới thiệu là nền tảng phát triển trên Postgres. Mỗi project được cấp một database Postgres riêng cho ứng dụng web, mobile hoặc AI. Đội phát triển nói họ đang dựng lại các tính năng của Firebase bằng những công cụ mã nguồn mở đủ chuẩn doanh nghiệp, và mã nguồn được phát hành theo giấy phép Apache-2.0.

Bên dưới là PostgreSQL, database có nguồn gốc từ năm 1986 và tuân thủ ACID từ năm 2001. Khi demo trở thành pilot, đội kỹ thuật của khách sẽ không phải học một loại database lạ.

Chi tiết đáng giá nhất với FDE là Auth dùng chính database Postgres của project. Thông tin người dùng nằm trong một schema riêng, ngay cạnh dữ liệu nghiệp vụ.

Auth hỗ trợ nhiều phương thức, kể cả đăng nhập qua mạng xã hội và qua số điện thoại thông qua nhà cung cấp bên thứ ba. Người dùng và dữ liệu ở cùng một nơi nên bạn bớt được một hệ thống phải nối.

## Ví dụ: tài xế chỉ thấy đơn của mình

Thử hình dung khách là một công ty giao hàng. Họ muốn mỗi tài xế đăng nhập vào là chỉ thấy những đơn được giao cho mình. Phần lõi của demo nằm gọn trong một bảng và một policy:

```sql
create table deliveries (
  id bigint generated always as identity primary key,
  driver_id uuid not null references auth.users(id),
  address text not null,
  status text not null default 'pending'
);

alter table deliveries enable row level security;

create policy "driver sees own deliveries"
  on deliveries for select
  using (auth.uid() = driver_id);
```

Theo tài liệu của Supabase, khi đi cùng các policy RLS, token đăng nhập giới hạn quyền truy cập database đến từng dòng. Tài xế nào gửi token của mình thì chỉ nhận về các dòng có `driver_id` trùng với họ. Bạn không phải viết thêm tầng phân quyền nào ở frontend.

Tiếp theo, khách muốn có ước tính thời gian giao. Edge Functions là các hàm TypeScript chạy phía server, được phân phối toàn cầu ở edge, gần người dùng. Chúng chạy trên Deno, một runtime mã nguồn mở có sẵn TypeScript:

```ts
Deno.serve(async (req) => {
  const { stops } = await req.json();
  const minutes = stops.length * 12; // giả định cho demo: 12 phút mỗi điểm dừng
  return new Response(JSON.stringify({ eta_minutes: minutes }), {
    headers: { "Content-Type": "application/json" },
  });
});
```

Bạn deploy hàm qua Dashboard, CLI hoặc MCP. Nếu làm từ terminal thì gõ `supabase functions deploy eta`. Công thức ở đây cố ý đơn giản, vì ở buổi đầu khách cần thấy luồng chạy trọn vẹn hơn là một thuật toán hay.

## Hai cách RLS làm hỏng demo

**Điểm mấu chốt:** Trước khi khách bấm thử, hãy kiểm tra xem ai được xem dòng dữ liệu nào.

Cách hỏng nhẹ hơn là bật RLS rồi quên viết policy. Tài liệu Supabase ghi rõ: khi RLS đã bật, API không trả về dữ liệu nào cho publishable key cho đến khi bạn tạo policy. Trong ví dụ trên, nếu bỏ lệnh `create policy`, tài xế đăng nhập thành công nhưng thấy 0 đơn, và khách sẽ nghĩ hệ thống bị lỗi.

Cách hỏng nặng hơn là quên bật RLS. Một bảng nằm trong schema được expose mà không có RLS thì bất kỳ role nào có grant trên bảng đều đọc và ghi được. Nếu demo chạy trên dữ liệu thật của khách, đây là sự cố bảo mật chứ không còn là lỗi hiển thị.

Vì vậy, hãy coi bước kiểm tra cuối là bắt buộc. Tạo hai tài khoản test, gán cho mỗi tài khoản vài đơn, rồi lần lượt đăng nhập bằng từng tài khoản. Mỗi tài khoản phải thấy đúng phần của mình, không hơn và không thiếu.

## Gói Free cho bạn bao nhiêu?

Gói Free có database 500 MB chạy trên CPU dùng chung với 500 MB RAM, cùng 50.000 người dùng hoạt động mỗi tháng. Với một bản demo, như vậy là dư. Giới hạn đáng để ý nằm ở chỗ khác.

Project Free bị tạm dừng sau 1 tuần không hoạt động, và mỗi tài khoản chỉ có tối đa 2 project đang chạy. Giả sử bạn demo vào thứ Hai và mười ngày sau mới có buổi họp tiếp: lúc đó project đã bị dừng. Hãy mở lại project từ hôm trước buổi họp, còn khi demo thành pilot thì chuyển sang gói Pro trả phí.

Cũng nên nhớ Edge Functions viết bằng TypeScript. Nếu logic sẵn có của khách viết bằng ngôn ngữ khác, bạn sẽ phải port sang TypeScript hoặc chạy phần đó ở nơi khác. Hãy hỏi điều này ngay trong buổi discovery.

## Học gì trước?

Hãy bắt đầu từ SQL và cách viết policy RLS, vì phần lớn rủi ro của demo nằm ở đó. Sau đó mới học CLI. CLI chạy được các dịch vụ Supabase ngay trên máy, quản lý project hosted từ terminal, hỗ trợ migration và schema dạng khai báo (declarative schema), nên bạn dựng lại được nguyên bản demo trong project của chính khách.

Nếu đang nhắm vai FDE, bạn có thể biến bài tập giao hàng ở trên thành một mục trong portfolio. Trong CV, đừng chỉ ghi "biết Supabase". Hãy kể kết quả: bạn đã dựng demo có đăng nhập, phân quyền theo từng dòng bằng RLS, chạy một hàm phía server, và mất bao lâu để làm xong.

Khách sẽ không nhớ bạn dùng công cụ gì. Họ nhớ lúc hai tài xế đăng nhập cạnh nhau và mỗi người chỉ thấy đơn của mình.

**Thử ngay tuần này:**

- Tạo một project Free, dựng bảng deliveries theo ví dụ, bật RLS rồi đăng nhập bằng hai tài khoản test để chắc mỗi tài khoản chỉ thấy dòng của mình.
- Chạy Supabase local bằng CLI, ghi schema thành migration, rồi dựng lại toàn bộ demo trên một project trống để đo xem mất bao lâu.
- Viết một Edge Function nhỏ bằng TypeScript và deploy bằng CLI, rồi gọi nó từ terminal.

## Nguồn

- [GitHub - supabase/supabase: The Postgres development platform.](https://github.com/supabase/supabase)

- [Edge Functions | Supabase Docs](https://supabase.com/docs/guides/functions)

- [Auth | Supabase Docs](https://supabase.com/docs/guides/auth)

- [Row Level Security | Supabase Docs](https://supabase.com/docs/guides/database/postgres/row-level-security)

- [Supabase Pricing](https://supabase.com/pricing)

- [Local Development & CLI | Supabase Docs](https://supabase.com/docs/guides/local-development)

- [PostgreSQL: About](https://www.postgresql.org/about/)

- [Deno, the drop-in JavaScript runtime for Node developers](https://deno.com/)
