🔌 API Reference

REST API tự động sinh bởi Supabase PostgREST — endpoints, RPC functions và code examples

📋 Tổng Quan API — Tài liệu Public

📖 Đây là tài liệu API công khai — không cần đăng nhập

Trang này chứa toàn bộ thông tin API của hệ thống phòng khám: schema 10 bảng, 10 RPC functions, code examples, hướng dẫn authentication. Lập trình viên có thể đọc và tích hợp ngay mà không cần tài khoản Supabase.

API sử dụng Supabase PostgREST — REST API được tự động sinh từ PostgreSQL schema.

40
REST Endpoints
10
RPC Functions
10
Database Tables
50
Tổng Endpoints
⚠️
Lưu ý: Supabase Dashboard (Table Editor, SQL Editor, v.v.) yêu cầu đăng nhập tài khoản Supabase của project owner. Các link admin tools ở cuối trang chỉ dành cho admin/team nội bộ.

🔑 Authentication — Cách xác thực API

Mọi request đều cần 2 headers:

apikey
SUPABASE_PUBLISHABLE_KEY
Publishable key (anon) — an toàn để dùng ở client
Authorization
Bearer SUPABASE_PUBLISHABLE_KEY
Hoặc Bearer JWT_TOKEN nếu user đã login

🌐 Base URL

https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1
text

🔑 API Key (lấy từ .env.local)

NEXT_PUBLIC_SUPABASE_URL=https://mrgdawvdnofcxamjvcbl.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxx
env
🚨
KHÔNG BAO GIỜ để lộ service_role key ở client-side. Chỉ dùng publishable key (anon key) cho frontend. Service role key chỉ dùng server-side (Server Actions, API Routes).

Ví dụ cURL xác thực

# Thay YOUR_ANON_KEY bằng publishable key thực tế
curl "https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/patients?select=patient_code,full_name&limit=3" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"
bash

🚀 Quick Start

1. Cài đặt

npm install @supabase/supabase-js @supabase/ssr
bash

2. Cấu hình .env.local

NEXT_PUBLIC_SUPABASE_URL=https://mrgdawvdnofcxamjvcbl.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_xxxxx
env

3. Tạo Supabase Client (Server Component)

import { createClient } from "@/lib/supabase/server";

// Trong Server Component hoặc Server Action
const supabase = await createClient();

// Query dữ liệu
const { data, error } = await supabase
  .from("patients")
  .select("*")
  .limit(10);
typescript

4. Tạo Supabase Client (Client Component)

import { createClient } from "@/lib/supabase/client";

// Trong Client Component
const supabase = createClient();

// Realtime subscription
supabase
  .channel("visits")
  .on("postgres_changes", { event: "INSERT", schema: "public", table: "visits" }, 
    (payload) => console.log("New visit:", payload)
  )
  .subscribe();
typescript

🌐 REST API Endpoints

Base URL: https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1
Headers: apikey + Authorization: Bearer KEY
departments
10
GET
/rest/v1/departments
POST
/rest/v1/departments
PATCH
/rest/v1/departments?id=eq.x
DELETE
/rest/v1/departments?id=eq.x
doctors
20
GET
/rest/v1/doctors
POST
/rest/v1/doctors
PATCH
/rest/v1/doctors?id=eq.x
DELETE
/rest/v1/doctors?id=eq.x
patients
5,000
GET
/rest/v1/patients
POST
/rest/v1/patients
PATCH
/rest/v1/patients?id=eq.x
DELETE
/rest/v1/patients?id=eq.x
visits
54,720
GET
/rest/v1/visits
POST
/rest/v1/visits
PATCH
/rest/v1/visits?id=eq.x
DELETE
/rest/v1/visits?id=eq.x
services
54
GET
/rest/v1/services
POST
/rest/v1/services
PATCH
/rest/v1/services?id=eq.x
DELETE
/rest/v1/services?id=eq.x
visit_services
104,156
GET
/rest/v1/visit_services
POST
/rest/v1/visit_services
PATCH
/rest/v1/visit_services?id=eq.x
DELETE
/rest/v1/visit_services?id=eq.x
medicines
40
GET
/rest/v1/medicines
POST
/rest/v1/medicines
PATCH
/rest/v1/medicines?id=eq.x
DELETE
/rest/v1/medicines?id=eq.x
prescriptions
89,896
GET
/rest/v1/prescriptions
POST
/rest/v1/prescriptions
PATCH
/rest/v1/prescriptions?id=eq.x
DELETE
/rest/v1/prescriptions?id=eq.x
invoices
52,291
GET
/rest/v1/invoices
POST
/rest/v1/invoices
PATCH
/rest/v1/invoices?id=eq.x
DELETE
/rest/v1/invoices?id=eq.x
icd_codes
40
GET
/rest/v1/icd_codes
POST
/rest/v1/icd_codes
PATCH
/rest/v1/icd_codes?id=eq.x
DELETE
/rest/v1/icd_codes?id=eq.x
Tổng: 40 endpoints (CRUD × 10 bảng) + 10 RPC = 50 API endpoints

RPC Functions (Stored Procedures)

Endpoint:POST /rest/v1/rpc/<fn_name>
Stored procedures trên PostgreSQL. Trả về 1 JSON response.
POSTfn_dashboard_summary
#1

Tổng hợp KPI: doanh thu, lượt khám, bệnh nhân, BHYT + so sánh kỳ trước

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON { total_revenue, total_visits, total_patients, new_patients, avg_revenue_per_visit, bhyt_visits, self_pay_visits, prev_revenue, prev_visits }
Example:
const { data } = await supabase.rpc("fn_dashboard_summary", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
// → { total_revenue: 3144119500, total_visits: 4720, ... }
typescript
POSTfn_revenue_by_period
#2

Doanh thu theo ngày/tuần/tháng với phân tách BHYT, tự chi, thuốc, dịch vụ

Parameters:
p_start_datedate
p_end_datedate
p_group_bytext
Returns:
JSON [{ period, total_revenue, bhyt_revenue, self_pay_revenue, medicine_revenue, service_revenue, visit_count, patient_count }]
Example:
const { data } = await supabase.rpc("fn_revenue_by_period", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-01-31",
  p_group_by: "week" // 'day' | 'week' | 'month'
});
typescript
POSTfn_revenue_by_department
#3

Doanh thu theo từng khoa/phòng ban

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON [{ department_name, department_code, total_revenue, bhyt_revenue, patient_revenue, visit_count, patient_count, avg_revenue }]
Example:
const { data } = await supabase.rpc("fn_revenue_by_department", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
typescript
POSTfn_patient_statistics
#4

Thống kê bệnh nhân đa chiều: giới tính, tuổi, tỉnh, loại khám, BN mới/tái khám

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON { by_gender, by_age_group, by_province, new_vs_returning, by_visit_type, daily_visits }
Example:
const { data } = await supabase.rpc("fn_patient_statistics", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
// → { by_gender: [{gender:"Nam",count:1524}, ...], by_age_group: [...] }
typescript
POSTfn_doctor_performance
#5

Hiệu suất bác sĩ: lượt khám, doanh thu, BN mới vs tái khám

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON [{ doctor_name, degree, department_name, visit_count, patient_count, total_revenue, avg_revenue_per_visit, new_visits, return_visits }]
Example:
const { data } = await supabase.rpc("fn_doctor_performance", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
typescript
POSTfn_top_services
#6

Top dịch vụ được sử dụng nhiều nhất

Parameters:
p_start_datedate
p_end_datedate
p_limitinteger
Returns:
JSON [{ service_name, service_code, category_name, department_name, usage_count, total_revenue, bhyt_revenue, avg_price }]
Example:
const { data } = await supabase.rpc("fn_top_services", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28",
  p_limit: 10
});
typescript
POSTfn_insurance_report
#7

Báo cáo BHYT chi tiết: tổng hợp, theo khoa, xu hướng tháng

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON { summary: {total_amount, total_bhyt, ...}, by_department: [...], monthly_trend: [...] }
Example:
const { data } = await supabase.rpc("fn_insurance_report", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
typescript
POSTfn_top_diagnoses
#8

Top chẩn đoán ICD-10 phổ biến nhất

Parameters:
p_start_datedate
p_end_datedate
p_limitinteger
Returns:
JSON [{ icd_code, diagnosis_name, category, visit_count, patient_count, departments }]
Example:
const { data } = await supabase.rpc("fn_top_diagnoses", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28",
  p_limit: 10
});
typescript
POSTfn_hourly_distribution
#9

Phân bổ lượt khám theo giờ trong ngày

Parameters:
p_start_datedate
p_end_datedate
Returns:
JSON [{ hour, visit_count, avg_per_day }]
Example:
const { data } = await supabase.rpc("fn_hourly_distribution", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28"
});
typescript
POSTfn_top_medicines
#10

Top thuốc kê đơn nhiều nhất

Parameters:
p_start_datedate
p_end_datedate
p_limitinteger
Returns:
JSON [{ medicine_name, generic_name, unit, category, total_quantity, total_revenue, prescription_count }]
Example:
const { data } = await supabase.rpc("fn_top_medicines", {
  p_start_date: "2026-01-01",
  p_end_date: "2026-02-28",
  p_limit: 10
});
typescript

🗄️ Database Schema

departmentsKhoa/Phòng ban
10 rows
id*
uuid
code*
varchar
name*
varchar
description
text
is_active
boolean
created_at
timestamptz
doctorsBác sĩ
20 rows
id*
uuid
employee_code*
varchar
full_name*
varchar
department_id
uuid
degree
varchar
specialty
varchar
phone
varchar
email
varchar
license_number
varchar
is_active
boolean
created_at
timestamptz
patientsBệnh nhân
5,000 rows
id*
uuid
patient_code*
varchar
full_name*
varchar
date_of_birth
date
gender
varchar
phone
varchar
address
text
ward
varchar
district
varchar
province
varchar
cccd
varchar
bhyt_number
varchar
bhyt_expiry
date
blood_type
varchar
allergies
text
emergency_contact
varchar
emergency_phone
varchar
created_at
timestamptz
visitsLượt khám
54,720 rows
id*
uuid
visit_code*
varchar
patient_id
uuid
doctor_id
uuid
department_id
uuid
visit_date*
timestamptz
visit_type
varchar
status
varchar
diagnosis
text
icd_code_id
uuid
payment_method
varchar
vital_signs
jsonb
notes
text
created_at
timestamptz
servicesDanh mục dịch vụ
54 rows
id*
uuid
code*
varchar
name*
varchar
department_id
uuid
category
varchar
unit
varchar
price*
numeric
bhyt_covered
boolean
bhyt_percentage
integer
is_active
boolean
created_at
timestamptz
visit_servicesChi tiết dịch vụ theo lượt khám
104,156 rows
id*
uuid
visit_id
uuid
service_id
uuid
quantity
integer
unit_price*
numeric
discount_percent
integer
bhyt_amount
numeric
patient_amount*
numeric
total_amount*
numeric
created_at
timestamptz
medicinesDanh mục thuốc
40 rows
id*
uuid
code*
varchar
name*
varchar
generic_name
varchar
unit
varchar
category
varchar
manufacturer
varchar
country
varchar
price*
numeric
bhyt_covered
boolean
is_active
boolean
created_at
timestamptz
prescriptionsĐơn thuốc
89,896 rows
id*
uuid
visit_id
uuid
medicine_id
uuid
quantity*
integer
dosage
varchar
duration_days
integer
unit_price*
numeric
total_price*
numeric
bhyt_amount
numeric
notes
text
created_at
timestamptz
invoicesHóa đơn
52,291 rows
id*
uuid
invoice_code*
varchar
visit_id
uuid
service_total
numeric
medicine_total
numeric
total_amount*
numeric
bhyt_amount
numeric
discount_amount
numeric
patient_amount*
numeric
payment_status
varchar
payment_method
varchar
payment_date
timestamptz
cashier_name
varchar
notes
text
created_at
timestamptz
icd_codesMã bệnh ICD-10
40 rows
id*
uuid
code*
varchar
name_vi*
varchar
name_en
varchar
category
varchar

💻 Code Examples

CRUD: Tạo bệnh nhân mới

const { data, error } = await supabase
  .from("patients")
  .insert({
    patient_code: "BN-20260001",
    full_name: "Nguyễn Văn A",
    date_of_birth: "1990-05-15",
    gender: "Nam",
    phone: "0901234567",
    province: "Hồ Chí Minh",
    bhyt_number: "HS4010012345678"
  })
  .select()    // Trả về record vừa tạo
  .single();   // Chỉ 1 record

console.log(data);
// → { id: "uuid...", patient_code: "BN-20260001", full_name: "Nguyễn Văn A", ... }
typescript

Query: Tìm bệnh nhân có BHYT sắp hết hạn

const { data } = await supabase
  .from("patients")
  .select("patient_code, full_name, bhyt_number, bhyt_expiry")
  .not("bhyt_number", "is", null)
  .lte("bhyt_expiry", "2026-03-31")
  .gte("bhyt_expiry", "2026-02-01")
  .order("bhyt_expiry", { ascending: true });

// → Danh sách BN có BHYT hết hạn trong tháng 2-3/2026
typescript

Join: Lấy lượt khám kèm thông tin bệnh nhân + bác sĩ

const { data } = await supabase
  .from("visits")
  .select(`
    visit_code,
    visit_date,
    status,
    diagnosis,
    patients ( patient_code, full_name, phone ),
    doctors ( full_name, degree ),
    departments ( name ),
    icd_codes ( code, name_vi )
  `)
  .eq("status", "hoan_thanh")
  .order("visit_date", { ascending: false })
  .limit(20);

// → Tự động JOIN 4 bảng liên quan qua Foreign Keys
typescript

Aggregate: Đếm lượt khám theo trạng thái

const { data } = await supabase
  .from("visits")
  .select("status")
  .gte("visit_date", "2026-02-01")
  .lte("visit_date", "2026-02-28");

// Hoặc dùng RPC function cho aggregate phức tạp:
const { data: summary } = await supabase.rpc("fn_dashboard_summary", {
  p_start_date: "2026-02-01",
  p_end_date: "2026-02-28"
});
typescript

Realtime: Theo dõi lượt khám mới

// Client Component ("use client")
const supabase = createClient();

const channel = supabase
  .channel("realtime-visits")
  .on(
    "postgres_changes",
    { event: "INSERT", schema: "public", table: "visits" },
    (payload) => {
      console.log("Lượt khám mới:", payload.new);
      // Update UI...
    }
  )
  .subscribe();

// Cleanup
return () => { supabase.removeChannel(channel); };
typescript

cURL: Gọi REST API trực tiếp

# Lấy 5 bệnh nhân
curl "https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/patients?limit=5&select=patient_code,full_name" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY"

# Gọi RPC function
curl -X POST "https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/rpc/fn_dashboard_summary" \
  -H "apikey: YOUR_ANON_KEY" \
  -H "Authorization: Bearer YOUR_ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"p_start_date":"2026-01-01","p_end_date":"2026-02-28"}'
bash

📄 OpenAPI Specification (Public)

✅ OpenAPI JSON Spec — truy cập công khai bằng API key

Supabase expose OpenAPI 2.0 spec tại endpoint REST. Dùng spec này để import vào Swagger UI, Postman, Insomnia, hoặc bất kỳ tool nào hỗ trợ OpenAPI — không cần login Supabase Dashboard.

Lấy OpenAPI spec

# OpenAPI JSON spec (public với apikey)
curl "https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/" \
  -H "apikey: YOUR_ANON_KEY" \
  | python -m json.tool > openapi-spec.json

# Mở bằng Swagger UI online:
# 1. Vào https://editor.swagger.io
# 2. File → Import File → chọn openapi-spec.json
# → Xem tất cả endpoints, schemas, try it live
bash

Import vào Postman

# Cách 1: Import URL trực tiếp trong Postman
# Collections → Import → Link → paste:
# https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/
# (thêm header apikey = YOUR_ANON_KEY)

# Cách 2: Import file
curl -o openapi.json "https://mrgdawvdnofcxamjvcbl.supabase.co/rest/v1/" \
  -H "apikey: YOUR_ANON_KEY"
# → Postman → Import → Upload Files → openapi.json
bash
🟢 Swagger UI

Import spec → xem & test endpoints trên browser

🟠 Postman

Import → tạo collection → set apikey variable

🟣 Insomnia

Import OpenAPI → test & share workspace

🔧 Admin Tools (cần đăng nhập Supabase)

🔒
Các link bên dưới yêu cầu đăng nhập Supabase Dashboard — chỉ dành cho project owner / admin team. Lập trình viên tích hợp API không cần truy cập các tool này.
Tài liệu API Public — Phòng Khám Đa Khoa | 10 bảng × 4 methods + 10 RPC = 50 endpoints | Powered by Supabase PostgREST