想像一下這個情況:你和同事一起開發一個專案,你在本地新增了一個 users 表格,但同事的資料庫裡沒有。
或者你想在正式環境部署時,確保資料庫結構和開發環境完全一樣。這時候就需要 Migration 來幫忙了!
在 Hasura 中,Migration 就像是「資料庫變更的說明書」,記錄了每一步資料庫的修改,讓團隊成員和不同環境都能保持同步。
簡單來說:
- 🎯 Migration = 資料庫變更的歷史記錄
- ⚡ 自動同步 不同環境的資料庫結構
- 🔄 可以回退 如果改錯了能夠復原
- 👥 團隊協作 大家的資料庫都保持一致
Hasura Migration 檔案在哪裡?
當你在 Hasura Console 點擊任何按鈕(建立表格、新增欄位等),Hasura 會自動在背景產生檔案。
檔案長這樣:
your-project/
├── migrations/
│ └── default/
│ ├── 1691234567890_init/
│ │ └── up.sql
│ ├── 1691234567891_create_table_users/
│ │ ├── up.sql ← 「往前做」的指令
│ │ └── down.sql ← 「往後退」的指令
│ └── 1691234567892_add_user_email/
│ ├── up.sql
│ └── down.sql
└── metadata/
├── tables.yaml ← 表格設定
└── databases.yaml ← 資料庫連線設定檔案名稱的秘密:
1691234567891_create_table_users/
│ │
│ └── 人類看得懂的描述
└── 時間戳記(電腦用來排順序)為什麼要用時間戳記?
- 確保每個人的 Migration 都按照正確順序執行
- 避免兩個開發者同時建立檔案時發生衝突
up.sql:「往前走」做了什麼?
up.sql 記錄的是「我要做什麼變更」。
實際情況 1:在 Console 建立一個新表格
你在 Console 做的事:
- 點擊「Create Table」
- 輸入表格名稱:
users - 新增欄位:
name,email - 點擊「Add Table」
Hasura 自動產生的 up.sql:
-- 建立 users 表格
CREATE TABLE "public"."users" (
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
"name" text NOT NULL,
"email" text NOT NULL,
"created_at" timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY ("id")
);白話翻譯:
- 建立一個叫
users的表格 - 有
id(自動產生)、name、email、created_at四個欄位 id是主鍵(每筆資料的唯一識別)
實際情況 2:為表格新增欄位
你在 Console 做的事:
- 進入
users表格 - 點擊「Add Column」
- 新增
phone欄位
Hasura 自動產生的 up.sql:
-- 為 users 表格新增 phone 欄位
ALTER TABLE "public"."users" ADD COLUMN "phone" text;白話翻譯:
- 在現有的
users表格新增一個phone欄位
實際情況 3:建立表格之間的關聯
你在 Console 做的事:
- 建立
posts表格 - 設定
author_id欄位 - 建立與
users表格的關聯
Hasura 自動產生的 up.sql:
-- 建立 posts 表格
CREATE TABLE "public"."posts" (
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
"title" text NOT NULL,
"content" text,
"author_id" uuid NOT NULL,
PRIMARY KEY ("id")
);
-- 建立關聯(posts 的 author_id 對應到 users 的 id)
ALTER TABLE "public"."posts"
ADD CONSTRAINT "posts_author_id_fkey"
FOREIGN KEY ("author_id") REFERENCES "public"."users"("id");白話翻譯:
- 建立
posts表格 - 每篇文章都有一個作者(透過
author_id連結到users)
down.sql:「往後退」怎麼復原?
down.sql 是 up.sql 的相反操作,用來撤銷剛才的變更。
對應上面三個情況的 down.sql:
情況 1 的 down.sql:刪除整個表格
-- 刪除 users 表格
DROP TABLE "public"."users";情況 2 的 down.sql:移除欄位
-- 從 users 表格移除 phone 欄位
ALTER TABLE "public"."users" DROP COLUMN "phone";情況 3 的 down.sql:刪除關聯和表格
-- 先刪除關聯
ALTER TABLE "public"."posts" DROP CONSTRAINT "posts_author_id_fkey";
-- 再刪除表格
DROP TABLE "public"."posts";為什麼需要 down.sql?
真實情況:
- 📱 你剛發布新功能到正式環境
- 🚨 發現有嚴重 bug,用戶無法登入
- ⏰ 現在是晚上 11 點,需要緊急修復
- 🔄 執行
down.sql快速回到上一個穩定版本
# 一鍵回退最後一次變更
hasura migrate apply --down 1為什麼 Hasura 需要兩種檔案?SQL + YAML 的分工合作
你可能會好奇:「為什麼不能只用一種檔案就好?為什麼要搞得這麼複雜?」
其實這是有原因的!讓我們用一個簡單的比喻來理解:
蓋房子的比喻
SQL 檔案 = 建築結構
- 地基、樑柱、牆壁(資料庫的表格、欄位、關聯)
- 這些是「硬體」,是房子的骨架
YAML 檔案 = 室內裝潢
- 家具擺設、電器配置、裝修風格(權限、GraphQL 設定、UI 配置)
- 這些是「軟體」,是房子的功能配置
實際差異對照
| 檔案類型 | 負責內容 | 舉例 |
|---|---|---|
| SQL 檔案 | 資料庫結構 | • 建立 users 表格• 新增 email 欄位• 設定外鍵關聯 |
| YAML 檔案 | Hasura 功能 | • 誰可以查看哪些資料 • GraphQL API 的名稱 • 自動觸發的事件 |
負責內容資料庫結構
舉例• 建立 users 表格• 新增 email 欄位• 設定外鍵關聯
負責內容Hasura 功能
舉例• 誰可以查看哪些資料• GraphQL API 的名稱• 自動觸發的事件
具體範例說明
情況:建立一個 users 表格
SQL 檔案處理:
-- 建立基本結構
CREATE TABLE "public"."users" (
"id" uuid NOT NULL DEFAULT gen_random_uuid(),
"name" text NOT NULL,
"email" text NOT NULL
);YAML 檔案處理:
# 設定這個表格在 Hasura 中的行為
- table:
name: users
schema: public
select_permissions:
- role: user
permission:
columns: [id, name, email]
filter: { id: { _eq: "X-Hasura-User-Id" } }為什麼要分開?
資料庫獨立性
- SQL 檔案可以在任何 PostgreSQL 資料庫執行
- 即使不用 Hasura,這些 SQL 也能正常運作
- 如果哪天要換別的工具,資料庫結構不會丟失
Hasura 特有功能
- YAML 檔案包含 Hasura 獨有的設定
- 權限控制、GraphQL 客製化、事件觸發等
- 這些功能其他資料庫工具不一定有
職責分離
- 資料庫管理員關心 SQL(結構)
- 應用程式開發者關心 YAML(功能)
- 不同角色的人可以專注在自己的領域
版本控制更清楚
- SQL 變更 = 資料庫結構改變(通常需要更謹慎)
- YAML 變更 = 功能設定調整(相對安全)
- 可以分別追蹤不同類型的變更
兩者如何協作?
完整的 Migration 流程:
- SQL 先執行
# 更新資料庫結構
hasura migrate apply- YAML 後套用
# 套用 Hasura 設定
hasura metadata apply為什麼要這個順序?
- 先有桌子(SQL),才能在桌子上放東西(YAML)
- 如果表格都沒有,就無法設定表格的權限
實際遇到的情況
場景:團隊新成員加入專案
只執行 SQL 會怎樣?
hasura migrate apply # ✅ 資料庫結構 OK
# 但是...沒有執行 metadata apply結果:
- ✅ 資料庫有正確的表格和欄位
- ❌ Hasura Console 看不到表格
- ❌ GraphQL API 無法使用
- ❌ 權限設定都消失了
正確做法:
hasura migrate apply # 先處理資料庫結構
hasura metadata apply # 再處理 Hasura 設定記憶小技巧
SQL = Structure(結構)
- 想到「SQL」就想到「Structure」
- 負責資料庫的骨架
YAML = Behavior(行為)
- 想到「YAML」就想到「行為設定」
- 負責 Hasura 的功能配置
YAML 檔案:Hasura 的設定檔
現在你知道為什麼需要 YAML 了,讓我們看看裡面實際放了什麼:
metadata/tables.yaml:表格的進階設定
- table:
name: users
schema: public
# 權限設定
select_permissions:
- role: user
permission:
columns: [id, name, email] # 用戶可以看到這些欄位
filter:
id: { _eq: "X-Hasura-User-Id" } # 只能看到自己的資料
# 關聯設定
object_relationships:
- name: posts
using:
foreign_key_constraint_on:
column: author_id
table: { name: posts, schema: public }這個設定的意思:
- 定義了
users表格的權限 - 一般用戶只能查看自己的個人資料
- 設定了與
posts表格的關聯
metadata/databases.yaml:資料庫連線
- name: default
kind: postgres
configuration:
connection_info:
database_url:
from_env: HASURA_GRAPHQL_DATABASE_URL # 從環境變數讀取連線字串白話翻譯:
- 告訴 Hasura 要連接哪個 PostgreSQL 資料庫
- 連線資訊放在環境變數裡(比較安全)
實戰:Migration 是怎麼運作的?
步驟 1:開發階段
小明在本地開發:
- 打開 Hasura Console
- 建立
users表格 → 產生 Migration A - 新增
posts表格 → 產生 Migration B - 把程式碼 push 到 Git
步驟 2:其他開發者同步
小華拉取程式碼:
# 拉取最新程式碼(包含 Migration 檔案)
git pull
# 套用 Migration 到本地資料庫
hasura migrate apply
hasura metadata apply結果: 小華的本地資料庫現在和小明的一模一樣!
步驟 3:部署到正式環境
# 在正式環境執行
hasura migrate apply --endpoint https://your-production-endpoint
hasura metadata apply --endpoint https://your-production-endpoint結果: 正式環境的資料庫也更新了!
常見問題與實際解決方法
問題 1:「我不小心刪錯表格了!」
解決方法:
# 回退到刪除表格之前的狀態
hasura migrate apply --down 1預防方法:
- 重要操作前先備份
- 在測試環境先試試看
問題 2:「團隊成員的資料庫不一致」
症狀:
- A 同事說:「咦?為什麼我這裡沒有
phone欄位?」 - B 同事說:「我的 GraphQL query 為什麼失敗?」
解決方法:
# 檢查 Migration 狀態
hasura migrate status
# 套用遺漏的 Migration
hasura migrate apply
hasura metadata apply問題 3:「正式環境部署出問題」
安全部署流程:
- 測試環境驗證
# 先在測試環境試跑
hasura migrate apply --endpoint https://test-endpoint- 備份正式環境
# 備份資料庫
pg_dump your_database > backup_$(date +%Y%m%d).sql- 部署到正式環境
hasura migrate apply --endpoint https://production-endpoint- 如果有問題立即回退
hasura migrate apply --down 1 --endpoint https://production-endpoint實戰練習:建立一個簡單的 Todo App
讓我們透過實際操作來理解 Migration!
目標:建立一個 Todo 應用程式
需要的表格:
users:使用者todos:待辦事項
步驟 1:建立 users 表格
在 Hasura Console 操作:
- 點擊 Data → Create Table
- 表格名稱:
users - 新增欄位:
name(Text, Required)email(Text, Required, Unique)
自動產生的檔案:
migrations/default/xxx_create_table_users/up.sqlmigrations/default/xxx_create_table_users/down.sql
步驟 2:建立 todos 表格
在 Hasura Console 操作:
- 建立
todos表格 - 新增欄位:
title(Text, Required)completed(Boolean, Default: false)user_id(UUID, Required)
- 建立與
users的關聯
自動產生的檔案:
migrations/default/yyy_create_table_todos/up.sqlmigrations/default/yyy_create_table_todos/down.sql
步驟 3:設定權限
在 Hasura Console 操作:
- 進入
todos表格的 Permissions 頁面 - 為
user角色設定權限:
- Select:只能看到自己的 todos
- Insert:只能新增自己的 todos
自動更新的檔案:
metadata/tables.yaml
步驟 4:測試和部署
本地測試:
- 用 GraphQL 查詢測試功能
- 確認權限設定正確
部署到測試環境:
hasura migrate apply --endpoint https://test-endpoint
hasura metadata apply --endpoint https://test-endpoint部署到正式環境:
hasura migrate apply --endpoint https://production-endpoint
hasura metadata apply --endpoint https://production-endpointMigration 的最佳實踐
命名要清楚
好的命名:
create_table_usersadd_phone_to_userscreate_user_posts_relationship
不好的命名:
update_dbfix_bugtemp_change
小步快跑
建議:
- 每次只做一件事
- 經常 commit 和 push
- 不要累積太多變更
避免:
- 一次建立 10 個表格
- 同時修改結構和資料
- 長時間不同步程式碼
測試再測試
部署前檢查清單:
- ✅ 在本地測試過
- ✅ 在測試環境驗證
- ✅ 確認 down.sql 能正常執行
- ✅ 備份正式環境資料
團隊溝通
重大變更時:
- 提前通知團隊成員
- 在團隊群組說明變更內容
- 提供 Migration 執行指令
總結:掌握 Migration 的關鍵要點
核心概念
- Migration = 資料庫變更的版本控制
- up.sql = 往前做什麼
- down.sql = 如何回退
- YAML = Hasura 的進階設定
實際操作
- 透過 Console 操作,讓 Hasura 自動產生檔案
- 使用
hasura migrate apply同步變更 - 遇到問題時用
--down回退
最佳實踐
- 小步快跑,經常同步
- 測試環境先驗證
- 重要操作前要備份
- 保持團隊溝通
下一步學習
- 試著建立一個簡單的專案
- 觀察每次 Console 操作產生的檔案
- 練習使用 CLI 指令
- 學習更進階的 Hasura 功能
記住,Migration 不是複雜的技術,而是讓團隊協作更順暢的工具。多動手練習,很快就能熟練運用!