Supabase
核心原則
1. Supabase 變更頻繁 — 在實作前請務必查閱變更日誌和當前文件。 請勿依賴訓練資料來了解 Supabase 功能。函式簽章、config.toml 設定和 API 慣例會在不同版本之間變更。
首先,擷取 https://supabase.com/changelog.md(一個輕量級的摘要索引 — 非大量擷取),掃描與你的任務相關的 breaking-change 標籤,並依照連結的頁面處理任何適用的變更。然後使用以下文件存取方法查找相關主題。
2. 驗證你的工作。 在實作任何修正後,執行一個測試查詢來確認變更有效。未經驗證的修正是遺漏不完整的。
3. 從錯誤中恢復,不要重複循環。 如果某個方法在嘗試 2-3 次後仍然失敗,請停止並重新考慮。嘗試不同的方法、查閱文件、更仔細地檢查錯誤,並在可取得時檢閱相關的記錄檔。Supabase 的問題並不總是能透過重複執行同一個命令來解決,而且答案也不總是在記錄檔中,但在繼續之前,記錄檔通常值得檢查。
4. 將資料表暴露給資料 API: 根據使用者的資料 API 設定,新建立的資料表可能不會自動透過資料 (REST) API 暴露。如果發生這種情況,則需要明確授予 anon 和 authenticated 角色存取權限。
請注意,這與 RLS 不同,RLS 控制的是資料表可存取後哪些資料列是可見的,而不是資料表是否可存取。
當使用者回報一個 SQL 建立的資料表意外無法存取時,請檢查他們的資料 API 設定,以及是否已透過明確的 GRANT SQL 授予角色存取權限。在授予公開 (anon/authenticated) 存取權限時,請同時啟用 RLS。請參閱將資料表暴露給資料 API 以了解完整的設定工作流程。
5. 在暴露的結構中的 RLS。
在任何暴露的結構(預設包含 public)中的每個資料表上啟用 RLS。這在 Supabase 中至關重要,因為當 anon/authenticated 角色擁有存取權限時(請參閱將資料表暴露給資料 API),暴露結構中的資料表可以透過資料 API 存取。對於私有結構,建議將 RLS 作為縱深防禦。啟用 RLS 後,建立符合實際存取模型的政策,而不是將每個資料表預設為相同的 auth.uid() 模式。
6. 安全檢查清單。 在處理任何涉及驗證、RLS、檢視、儲存或用戶資料的 Supabase 任務時,請執行此檢查清單。這些是 Supabase 特有的安全陷阱,會無聲地製造漏洞:
-
驗證和工作階段安全
- 絕不要在基於 JWT 的授權決策中使用
user_metadata宣告。 在 Supabase 中,raw_user_meta_data是由使用者編輯的,並且可以出現在auth.jwt()中,因此對於 RLS 政策或任何其他授權邏輯來說是不安全的。請改將授權資料儲存在raw_app_meta_data/app_metadata中。 - 刪除使用者並不會使現有的存取權杖失效。 先登出或撤銷工作階段,將敏感應用的 JWT 有效期設短,並在敏感操作上針對
auth.sessions驗證session_id以獲得嚴格保證。 - 如果你使用
app_metadata或auth.jwt()進行授權,請記住 JWT 宣告在使用者的權杖刷新前不一定是新的。
- 絕不要在基於 JWT 的授權決策中使用
-
API 金鑰和客戶端暴露
- 絕不在公開客戶端中暴露
service_role或密鑰。 前端程式碼應優先使用可發布金鑰。舊版的anon金鑰僅用於相容性。在 Next.js 中,任何NEXT_PUBLIC_環境變數都會被發送到瀏覽器。 - 對管理 API、CLI 和 MCP 伺服器使用範圍限定的個人存取權杖。 傳統的個人存取權杖攜帶著使用者在所有組織和專案上的完整帳戶存取權限。對於腳本、CI 和代理程式,請讓使用者建立一個範圍限定的權杖,限制在任務所需的組織、專案和權限內。個人存取權杖指南列出了每個管理 API 端點和 MCP 工具所需的權限。請在那裡查詢權限,而不是用猜的。
- 絕不在公開客戶端中暴露
-
RLS、檢視和特權資料庫程式碼
- 檢視預設會繞過 RLS。 在 Postgres 15 及以上版本,使用
CREATE VIEW ... WITH (security_invoker = true)。在較舊版本的 Postgres 中,透過撤銷anon和authenticated角色的存取權限,或將它們放在未暴露的結構中來保護你的檢視。 - UPDATE 需要一個 SELECT 政策。 在 Postgres RLS 中,UPDATE 需要先 SELECT 該資料列。如果沒有 SELECT 政策,更新會默默地回傳 0 行 — 沒有錯誤,只是沒有改變。
auth.role()已被棄用 — 請改用TO子句。 Supabase 已棄用auth.role(),改為直接在政策上使用TO authenticated或TO anon指定目標角色。除了棄用之外,當啟用匿名登入時,auth.role() = 'authenticated'會默默失效,因為匿名使用者帶有authenticatedPostgres 角色,無論使用者是否真正登入,都會通過檢查。-- 已棄用(請勿使用) create policy "example" on table_name for select using ( auth.role() = 'authenticated' );- 僅使用
TO authenticated是未經授權的驗證(BOLA / IDOR)。 僅使用TO authenticated只檢查角色 — 它不會限制使用者可以存取哪些資料列。正確的模式是將TO authenticated與USING中的所有權謂詞結合:create policy "example" on table_name for select to authenticated using ( (select auth.uid()) = user_id ); - UPDATE 政策需要
USING和WITH CHECK。 如果沒有WITH CHECK,使用者可以將資料列的user_id重新指派給另一個使用者:create policy "example" on table_name for update to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id ); SECURITY DEFINER函式會繞過 RLS。SECURITY DEFINER函式以其建立者的權限執行 — 通常是具有bypassrls的角色(例如postgres)。絕不要為了解決權限錯誤而新增SECURITY DEFINER;它只會默默地移除存取控制,而不解決根本原因。請優先使用SECURITY INVOKER。public中的SECURITY DEFINER函式可由所有角色呼叫。 Postgres 預設會為每個新函式授予PUBLIC的EXECUTE權限,因此public中的任何SECURITY DEFINER函式都是一個公開的 API 端點,anon和authenticated(它們繼承自PUBLIC)無需任何額外授權即可呼叫。當確實需要SECURITY DEFINER時(例如,繞過內部查找表的 RLS),請將該函式保留在未暴露的結構中,始終在函式本體中包含auth.uid()檢查,並在做出變更後執行supabase db advisors。
- 檢視預設會繞過 RLS。 在 Postgres 15 及以上版本,使用
-
儲存存取控制
- 儲存 upsert 需要 INSERT + SELECT + UPDATE。 僅授予 INSERT 允許新的上傳,但檔案取代(upsert)會默默失敗。你需要全部三個。
-
依賴項和供應鏈安全
- 安裝 Supabase 套件時,務必鎖定套件版本並提交鎖定檔(如
supabase-js、@supabase/ssr、supabase-py等)。請參閱 npm 安全指南 以取得完整的檢查清單。
- 安裝 Supabase 套件時,務必鎖定套件版本並提交鎖定檔(如
對於上述未涵蓋的任何安全問題,請擷取 Supabase 產品安全索引:https://supabase.com/docs/guides/security/product-security.md
Supabase CLI
總是透過 --help 來發現命令 — 絕不要猜測。CLI 結構在不同版本之間會變更。
supabase --help # 所有頂層命令
supabase <group> --help # 子命令(例如,supabase db --help)
supabase <group> <command> --help # 特定命令的標誌
Supabase CLI 已知陷阱:
supabase db query需要 CLI v2.79.0+ → 請使用 MCPexecute_sql或psql作為備援方案supabase db advisors需要 CLI v2.81.3+ → 請使用 MCPget_advisors作為備援方案supabase login的瀏覽器流程會建立一個帶有完整帳戶存取權限的傳統權杖。在 CI 中,請改將SUPABASE_ACCESS_TOKEN設定為範圍限定的個人存取權杖。它優先於任何已儲存的登入。使用資料庫密碼連接的命令(例如--password或SUPABASE_DB_PASSWORD,如supabase db push)不受權杖權限的限制。- 在命令式遷移專案中,首先使用
supabase migration new <name>建立新的手動編寫遷移檔案。絕不要發明一個遷移檔名或依賴記憶來記住預期格式。宣告式結構專案會從supabase/schemas/產生遷移;請參閱下方「進行和提交結構變更」。
版本檢查和升級: 執行 supabase --version 來檢查。如需 CLI 變更日誌和特定版本的功能,請查閱 CLI 文件 或 GitHub 發佈頁面。
Supabase MCP 伺服器
如需設定說明、伺服器 URL 和組態,請參閱 MCP 設定指南。
疑難排解連線問題 — 請依序執行以下步驟:
-
檢查伺服器是否可達:
curl -so /dev/null -w "%{http_code}" https://mcp.supabase.com/mcp預期會收到401(沒有權杖),表示伺服器正在執行。逾時或「連線被拒絕」表示伺服器可能已關閉。 -
檢查
.mcp.json組態: 確認專案根目錄有一個有效的.mcp.json,其中包含正確的伺服器 URL。如果遺失,請建立一個指向https://mcp.supabase.com/mcp的檔案。 -
驗證 MCP 伺服器: 如果伺服器可達且
.mcp.json正確,但工具不可見,則使用者需要進行驗證。Supabase MCP 伺服器使用 OAuth 2.1 — 告訴使用者在他們的代理程式中觸發驗證流程,在瀏覽器中完成它,然後重新載入工作階段。 在 CI 或其他無法執行瀏覽器流程的環境中,請改為在Authorization: Bearer標頭中傳遞一個範圍限定的個人存取權杖。 -
檢查範圍限定的權杖權限: 如果在使用範圍限定的個人存取權杖時工具失敗,並顯示「你沒有執行此動作的權限」,表示該權杖缺少該工具所需的權限,或者該專案不在權杖的範圍內。請在個人存取權杖指南中檢查該工具所需的權限。
Supabase 文件
在實作任何 Supabase 功能之前,請先找到相關的文件。請依優先順序使用以下方法:
- MCP
search_docs工具(首選 — 直接回傳相關片段) - 以 Markdown 格式擷取文件頁面 — 任何文件頁面都可以透過在 URL 路徑後附加
.md來擷取。 - 針對 Supabase 特定主題進行網路搜尋,當你不知道要看哪個頁面時。
進行和提交結構變更
首先決定專案使用哪種結構工作流程。
選項 A:宣告式結構
當 supabase/schemas/ 存在或 config.toml 設定了 schema_paths 時使用此選項。在這些檔案中編輯所需的結構狀態,然後產生並審查遷移。不要從手寫遷移開始。請參閱宣告式資料庫結構指南。
選項 B:命令式遷移
當專案不使用宣告式結構時使用此選項。
若要進行結構變更,請使用 execute_sql(MCP)或 supabase db query(CLI)。 這些方法會直接在資料庫上執行 SQL,而不會建立遷移歷史記錄條目,因此你可以自由地反覆迭代,並在準備就緒時產生一個乾淨的遷移。
請勿使用 apply_migration 來變更本地資料庫結構 — 它會在每次呼叫時寫入一個遷移歷史記錄條目,這意味著你無法反覆迭代,並且 supabase db diff / supabase db pull 將產生空白或衝突的差異。如果你使用它,你將被卡在你第一次嘗試時傳入的任何 SQL。
準備好提交 你的變更到遷移檔案時:
- 執行顧問程式 →
supabase db advisors(CLI v2.81.3+)或 MCPget_advisors。修復任何問題。 - 審查上述的安全檢查清單,如果你的變更涉及檢視、函式、觸發器或儲存。
- 產生遷移 →
supabase db pull <描述性名稱> --local --yes - 驗證 →
supabase migration list --local
偵錯
當你在與 Supabase 相關的請求中遇到錯誤時,例如來自 Supabase REST API、Postgres 資料庫或 PostgREST 的錯誤碼、空結果、意外被 RLS 封鎖、或來自 Supabase 服務(如 Auth、Realtime、Edge Functions 或 Storage)的錯誤,你必須在診斷或提出修復方案之前擷取 Supabase 的監控與偵錯文件,而不是依賴記憶。相同的文件也涵蓋了效能最佳化,例如慢查詢和遺失的索引。
參考指南
- 技能回饋 → references/skill-feedback.md 當使用者回報此技能提供了錯誤的指導或遺漏資訊時,必須閱讀