Thay thế Supabase bằng Hasura: tạo GraphQL API realtime trên PostgreSQL
Supabase nổi tiếng vì “Firebase mã nguồn mở”: PostgreSQL, Auth, Storage, Realtime, Edge Functions, dashboard tiện. Nhưng không phải dự án nào cũng cần toàn bộ bộ công cụ đó. Nếu trọng tâm của bạn là PostgreSQL + API dữ liệu mạnh + realtime + phân quyền chi tiết, Hasura có thể là lựa chọn gọn, sâu, kiểm soát tốt hơn.
Hasura không thay thế 1:1 mọi tính năng Supabase. Nó không mặc định cung cấp Auth, Storage, Functions theo kiểu “all-in-one”. Đổi lại, Hasura cực mạnh ở lớp API: tự sinh GraphQL API realtime từ PostgreSQL, hỗ trợ query/mutation/subscription, permission theo role, event trigger, remote schema, actions. Với team đã có DB PostgreSQL, auth riêng, backend riêng, Hasura thường là “lớp API dữ liệu” rất hiệu quả.
Bài viết này phân tích cách dùng Hasura thay Supabase ở phần database API + realtime, kiến trúc đề xuất, cách cấu hình, phân quyền, realtime subscription, migration, ưu/nhược điểm thực tế.
Hasura khác Supabase ở điểm nào?
Supabase: nền tảng backend đầy đủ
Supabase gồm nhiều mảnh:
– PostgreSQL: DB chính. – PostgREST: REST API tự động. – Realtime: nghe thay đổi DB. – Auth: email/password, OAuth, magic link. – Storage: file/object storage. – Edge Functions: serverless logic. – Dashboard: quản trị.
Supabase phù hợp khi muốn khởi động nhanh, ít tự vận hành, cần backend hoàn chỉnh.
Hasura: GraphQL engine trên PostgreSQL
Hasura tập trung vào:
– Tự sinh GraphQL query/mutation từ schema PostgreSQL. – Subscription realtime qua WebSocket. – Permission theo role, row-level, column-level. – Event trigger khi DB thay đổi. – Actions để gọi business logic ngoài. – Remote schema để ghép nhiều GraphQL API. – Metadata/migration quản lý bằng CLI.
Hasura phù hợp khi:
– Muốn GraphQL thay REST. – DB model phức tạp, cần quan hệ sâu. – Cần phân quyền dữ liệu tinh vi. – Muốn realtime trực tiếp trên query GraphQL. – Muốn giữ PostgreSQL riêng, không phụ thuộc platform.
Khi nào nên thay Supabase bằng Hasura?
Nên cân nhắc nếu
– Ứng dụng dùng PostgreSQL làm nguồn dữ liệu chính. – Frontend cần query linh hoạt: nested relation, aggregate, filter sâu. – Team thích GraphQL, codegen TypeScript, typed operation. – Cần realtime theo điều kiện query, không chỉ nghe event chung. – Có hệ thống auth riêng: Auth0, Clerk, Keycloak, custom JWT. – Muốn tách rõ: DB, Auth, Storage, API engine.
Không nên nếu
– Bạn cần giải pháp “mở lên dùng ngay” cho auth/storage/function. – Team chưa quen GraphQL. – API public cần cache HTTP REST đơn giản. – Realtime cần logic nghiệp vụ phức tạp ngoài DB. – Muốn tránh thêm một service phải vận hành.
Tóm lại: Supabase → backend platform. Hasura → data API layer. Thay thế hợp lý khi phần bạn cần từ Supabase chủ yếu là DB API + realtime.
Kiến trúc đề xuất
Một kiến trúc phổ biến:
Frontend/App
↓ GraphQL over HTTP/WebSocket
Hasura GraphQL Engine
↓ SQL
PostgreSQL
↓ triggers/events
Webhook/Worker/Backend serviceAuth có thể đứng ngoài:
User → Auth Provider → JWT
Frontend → Hasura kèm Authorization: Bearer <jwt>
Hasura → kiểm tra claims → áp permissionStorage tách riêng:
S3/R2/MinIO → lưu file
PostgreSQL → lưu metadata
Hasura → quản lý metadata qua GraphQLBusiness logic:
– Logic đơn giản → PostgreSQL function/view. – Logic cần gọi API ngoài → Hasura Action. – Logic async → Event Trigger + worker.
Cài đặt Hasura với PostgreSQL
Chạy local bằng Docker Compose:
version: "3.8"
services:
postgres:
image: postgres:16
restart: always
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: app
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
hasura:
image: hasura/graphql-engine:v2.42.0
restart: always
ports:
- "8080:8080"
depends_on:
- postgres
environment:
HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgres@postgres:5432/app
HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
HASURA_GRAPHQL_ADMIN_SECRET: "dev-secret"
HASURA_GRAPHQL_DEV_MODE: "true"
HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
volumes:
pgdata:
Chạy:
docker compose up -dMở:
http://localhost:8080Nhập admin secret:
dev-secretThiết kế schema PostgreSQL cho GraphQL tốt
Ví dụ app task realtime:
create table public.users (
id uuid primary key default gen_random_uuid(),
email text not null unique,
name text,
created_at timestamptz not null default now()
);
create table public.projects (
id uuid primary key default gen_random_uuid(),
name text not null,
owner_id uuid not null references public.users(id),
created_at timestamptz not null default now()
);
create table public.tasks (
id uuid primary key default gen_random_uuid(),
project_id uuid not null references public.projects(id) on delete cascade,
title text not null,
status text not null default 'todo',
assignee_id uuid references public.users(id),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);
Trong Hasura Console:
– Vào Data.
– Track tables: users, projects, tasks.
– Track relationships:
– projects.owner_id → users.id
– tasks.project_id → projects.id
– tasks.assignee_id → users.id
Sau đó Hasura tự tạo GraphQL API.
Query ví dụ:
query GetProjects {
projects {
id
name
owner {
id
email
}
tasks {
id
title
status
}
}
}Mutation:
mutation AddTask($projectId: uuid!, $title: String!) {
insert_tasks_one(object: {
project_id: $projectId,
title: $title
}) {
id
title
status
}
}Realtime GraphQL bằng Subscription
Điểm mạnh lớn của Hasura: subscription giống query, nhưng realtime.
subscription WatchProjectTasks($projectId: uuid!) {
tasks(
where: { project_id: { _eq: $projectId } }
order_by: { created_at: desc }
) {
id
title
status
assignee {
id
name
}
updated_at
}
}Khi có insert/update/delete trong tasks, client nhận dữ liệu mới qua WebSocket.
Trong frontend Apollo Client:
import { ApolloClient, InMemoryCache, split, HttpLink } from "@apollo/client";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
import { getMainDefinition } from "@apollo/client/utilities";
const httpLink = new HttpLink({
uri: "http://localhost:8080/v1/graphql",
headers: {
"x-hasura-admin-secret": "dev-secret"
}
});
const wsLink = new GraphQLWsLink(createClient({
url: "ws://localhost:8080/v1/graphql",
connectionParams: {
headers: {
"x-hasura-admin-secret": "dev-secret"
}
}
}));
const splitLink = split(
({ query }) => {
const def = getMainDefinition(query);
return def.kind === "OperationDefinition" && def.operation === "subscription";
},
wsLink,
httpLink
);
export const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache()
});
Lưu ý: admin secret chỉ dùng dev/server. Production phải dùng JWT + role permission.
Phân quyền: phần thay thế RLS của Supabase
Supabase hay dùng PostgreSQL RLS. Hasura dùng permission engine riêng dựa trên role + JWT claims.
Ví dụ JWT chứa:
{
"sub": "user-uuid",
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": "user-uuid"
}
}Trong Hasura, set permission cho role user.
Select projects
User chỉ thấy project mình sở hữu:
{
"owner_id": {
"_eq": "X-Hasura-User-Id"
}
}Select tasks
User chỉ thấy task thuộc project của mình:
{
"project": {
"owner_id": {
"_eq": "X-Hasura-User-Id"
}
}
}Insert tasks
Cho phép insert nếu project thuộc user:
{
"project": {
"owner_id": {
"_eq": "X-Hasura-User-Id"
}
}
}Column preset:
created_by → X-Hasura-User-IdƯu điểm: permission đọc được ngay trong Hasura Console, gắn với GraphQL operation. Nhược điểm: nếu app còn truy cập DB ngoài Hasura, bạn vẫn nên dùng PostgreSQL RLS hoặc giới hạn DB user.
Auth: thay Supabase Auth bằng gì?
Hasura không phải auth provider. Các lựa chọn:
– Auth0: OAuth/OIDC mạnh, enterprise tốt. – Clerk: DX tốt, frontend hiện đại. – Keycloak: open-source, tự host. – Firebase Auth: dễ dùng. – Custom auth service: tự phát JWT.
Cấu hình JWT cho Hasura:
HASURA_GRAPHQL_JWT_SECRET: '{"type":"HS256","key":"your-secret"}'Hoặc RS256/JWKS:
HASURA_GRAPHQL_JWT_SECRET: '{"type":"RS256","jwk_url":"https://example.com/.well-known/jwks.json"}'Flow chuẩn:
1. User đăng nhập qua auth provider.
2. Frontend nhận access token.
3. Token chứa Hasura claims.
4. Frontend gọi GraphQL kèm Authorization.
5. Hasura kiểm JWT, lấy role/user-id, áp permission.
Event Trigger: thay Edge Function cho tác vụ DB-driven
Supabase Edge Functions xử lý logic serverless. Với Hasura, tác vụ phát sinh từ DB có thể dùng Event Trigger.
Ví dụ: khi task được tạo, gọi webhook gửi email/slack.
Trong Hasura Console:
– Events → Create.
– Table: tasks.
– Operation: insert.
– Webhook: https://api.example.com/hasura/events/task-created.
Payload gửi tới backend gồm row mới, operation, table, trigger name.
Backend xử lý:
app.post("/hasura/events/task-created", async (req, res) => {
const task = req.body.event.data.new;
await notifyAssignee(task);
res.json({ ok: true });
});Ưu điểm: DB thay đổi → event chắc chắn được ghi nhận. Hasura có retry. Phù hợp async job, notification, sync search index.
Migration từ Supabase sang Hasura
Nếu đang dùng Supabase PostgreSQL, phần DB gần như giữ nguyên.
Bước chính
1. Export schema/data từ Supabase nếu chuyển DB. 2. Kết nối Hasura tới PostgreSQL hiện tại hoặc DB mới. 3. Track tables/views/functions. 4. Tạo relationships. 5. Chuyển RLS policy sang Hasura permissions. 6. Thay REST/Supabase client bằng GraphQL client. 7. Thay realtime channel bằng GraphQL subscription. 8. Tách auth/storage/functions sang dịch vụ tương ứng.
Mapping nhanh
– Supabase table API → Hasura query/mutation. – Supabase realtime → Hasura subscription. – Supabase RLS → Hasura permission hoặc PostgreSQL RLS + Hasura. – Supabase Auth → Auth0/Clerk/Keycloak/custom JWT. – Supabase Storage → S3/R2/MinIO. – Supabase Edge Functions → Actions/Event Triggers/backend service.
Hiệu năng và vận hành
Hasura không làm DB nhanh hơn. Nó sinh SQL tốt, nhưng hiệu năng vẫn phụ thuộc:
– Index đúng cột filter/order. – Query GraphQL không quá sâu. – Giới hạn limit mặc định. – Permission không quá phức tạp. – PostgreSQL đủ connection. – Dùng connection pooler nếu tải cao.
Nên cấu hình:
– Depth limit cho GraphQL. – Rate limit ở gateway/proxy. – Allowlist/Persisted queries cho production. – Read replica nếu đọc nhiều. – Observability: logs, metrics, tracing. – Metadata migration bằng Hasura CLI, không cấu hình tay trên production.
Ví dụ index:
create index tasks_project_id_created_at_idx
on public.tasks (project_id, created_at desc);
create index projects_owner_id_idx
on public.projects (owner_id);
Ưu điểm và đánh đổi
Ưu điểm
– GraphQL API sinh tự động, rất nhanh. – Realtime subscription mạnh, tự nhiên với frontend. – Permission chi tiết theo role, row, column. – Tận dụng PostgreSQL quan hệ sâu. – Tách khỏi vendor backend all-in-one. – Hợp với team cần typed API, codegen, schema-first-ish workflow.
Đánh đổi
– Phải tự chọn auth/storage/functions. – Cần hiểu GraphQL, PostgreSQL, permission. – Thêm service vận hành. – Realtime subscription có chi phí DB/WebSocket. – Business logic phức tạp nên để ngoài Hasura, tránh nhồi hết vào DB/API layer.
Kết luận thực tế
Thay Supabase bằng Hasura không phải “đổi logo backend”. Đây là đổi triết lý.
Nếu bạn cần một nền tảng đầy đủ, auth/storage/realtime/function có sẵn, Supabase vẫn rất đáng dùng. Nếu bạn đã có PostgreSQL, muốn GraphQL API realtime mạnh, cần phân quyền dữ liệu sâu, muốn tự ghép auth/storage/backend theo kiến trúc riêng, Hasura là lựa chọn rất sáng.
Công thức thực tế:
– PostgreSQL giữ dữ liệu. – Hasura làm GraphQL realtime API. – Auth provider phát JWT chứa Hasura claims. – S3/R2/MinIO lưu file. – Backend worker xử lý logic phức tạp qua Actions/Event Triggers. – Hasura permissions bảo vệ dữ liệu theo role/user.
Với cách này, bạn có được tốc độ phát triển gần như backend-as-a-service, nhưng vẫn giữ quyền kiểm soát kiến trúc, dữ liệu, phân quyền, realtime. Hasura không thay thế toàn bộ Supabase. Nó thay thế rất tốt phần quan trọng nhất với nhiều app dữ liệu: API realtime trực tiếp trên PostgreSQL.
Bình luận (0)
Chưa có bình luận. Hãy là người đầu tiên!