Thay Supabase bằng Hasura: GraphQL realtime cực nhanh cho PostgreSQL

P P T P Chung

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 service

Auth có thể đứng ngoài:

User → Auth Provider → JWT
Frontend → Hasura kèm Authorization: Bearer <jwt>
Hasura → kiểm tra claims → áp permission

Storage tách riêng:

S3/R2/MinIO → lưu file
PostgreSQL → lưu metadata
Hasura → quản lý metadata qua GraphQL

Business 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 -d

Mở:

http://localhost:8080

Nhập admin secret:

dev-secret

Thiế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.idtasks.project_id → projects.idtasks.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.

Tác giả

P T P

Chia sẻ

Bài viết liên quan

Bình luận (0)

Email của bạn sẽ không được hiển thị công khai.

Chưa có bình luận. Hãy là người đầu tiên!