# supabase outline ## overview background ```mermaid flowchart LR Front subgraph BaaS Back DBMS DB end Front-->Back-->DB DBMS-->DB ``` ref * [我獨自開發 - Supabase 打造全端應用 系列](https://ithelp.ithome.com.tw/users/20147822/ironman/8754?page=2) * 必看 以下是實際的本篇,建議應該先看所需功能的 user story 再連結對應的 function。 ### function of supabase * development and git version control * schema * RLS(row loevel security) * API * supbase 必定是直接從 frontend call 這些 API的。 * pure backend API (edge function) * RMDBS * RPC * object storage service (OSS) * 圖片,文檔... * no file will directly put in file system of computer. only put in DB or OSS. * realtime * ~~graphql~~ (don't use) * hosting * deploy ### supabase in user story of app development * git version control when modified in GUI * authentication and security * solve n+1 problem * subscribe and realtime update ## function ### development and git version control Supabase 支援低程式碼(low-code)操作,但底層仍需有原始碼(source code)才能進行版本控制。以下詳細解答基本問題,並說明具體工作流程。 * 如何在 local 產生 supabase project? * local supabase 如何連接到 online GUI? * online GUI 操作/修改 schema 後,是否會自動更新 local 的 schema? * 有何機制?何時不同步? #### 如何在 local 產生 supabase project? 使用 Supabase CLI 工具在本地初始化專案: 1. 安裝 Supabase CLI:`npx supabase --help` 2. 初始化專案:`supabase init` - 這會在當前目錄建立 `supabase/` 資料夾,包含設定檔案和遷移腳本。 3. 啟動本地服務:`supabase start` - 這會啟動**本地** PostgreSQL 資料庫、Auth 服務等,**模擬** Supabase 環境。 - > supabase start (using the CLI) is an opinionated, developer-focused wrapper around a specific docker compose file that's optimized for local development, whereas running docker compose up -d manually refers to a self-hosting/production setup #### tables create 流程 完全不寫 schema sql 的情況 1. edit table by supabase table editor web UI. 2. `npx supabase db diff -f ${task}` 1. this will automatically generate the migration files to reach edited tables 3. `npx supabase db reset` will clean the db and apply all migration files to verify if migration file is correct 但並不建議,還是有寫 schema 比較好,見下。 --- So can schema files directly create rather than pure migration files? [Declarative database schemas](https://supabase.com/docs/guides/local-development/declarative-database-schemas) tables 雖然可以用 `supabase db dump > supabase/schemas/prod.sql` 直接 dump 下來。 supbase 主要還是希望有寫 schemas,並且RD 自己知道要在 schemas change 時call diff 取得 migration。 先用 gui 操作,再用 `npx supabase db dump --local > schemas/prod.sql` 得到也可以。但得到的 schema 老實說很醜,不如手打。 或是 `supabase db pull [--local]` 可以和 web UI editor 有更好的連動。 ##### test data [Seeding your database](https://supabase.com/docs/guides/local-development/seeding-your-database) #### local supabase 如何連接到 online GUI? 1. 在 Supabase 儀表板建立專案,取得專案 ID(project reference)。 2. 使用 CLI 連結:`supabase link --project-ref <your-project-id>` - 這會將本地專案連結到遠端專案,允許同步資料庫 schema 和遷移。 - 想像為 `git remote add origin <repo-url>` 的概念。 ##### online GUI 操作/修改 schema 後,是否會自動更新 local 的 schema? 不會自動更新,但有機制手動同步: - **同步機制**: - 使用 `supabase db pull` 從遠端拉取最新的 schema,並生成遷移檔案。 - 或者使用 `supabase db diff` 比較本地與遠端差異,生成遷移。 - 遷移檔案存放在 `supabase/migrations/` 資料夾中,可提交到 Git。 - **何時不同步**: - 當您在 GUI 修改 schema 時,遠端資料庫立即更新,但本地不會自動反映。 - 如果團隊成員在 GUI 修改而未更新遷移,本地開發者需手動拉取最新變更。 - 建議所有 schema 變更都通過遷移檔案進行,以保持一致性。 ##### pull vs diff * `supabase db diff` * Purpose: Generates a migration file by comparing your local schema definition (typically in supabase/migrations or supabase/schemas) with a target database (local or remote). * `supabase db pull [--local]` * Purpose: Fetches the current schema from a remote Supabase database and generates a new migration file representing that schema. 目前是有遇到在 UI 改完後,`pull --local` 沒反應,可能的原因。 > Why it probably says “No schema changes found” in your repo? > > Your config.toml uses [db.migrations].schema_paths (declarative schema mode). In this mode, the CLI builds a shadow DB from supabase/schemas/*.sql and compares against it. If you modified tables via Studio, the declarative files still “win,” so pull/diff often detect nothing. 總之,這絕對是 bug,目前不建議依賴 `db pull` 系列的工作流程。至少 schema 部份不要。 #### 完整開發工作流程 1. **本地開發**: - 修改 `supabase/schemas/` 中的 SQL 檔案定義 schema。 - `supabase/migrations/`也可,但差了一點。 - 運行 `supabase db reset` 應用遷移到本地資料庫。 2. **推送變更**: - 使用 `supabase db push` 將本地遷移推送到遠端專案。 3. **GUI 修改後同步**: - 如果在 GUI 修改,運行 `supabase db pull` 拉取變更並生成遷移檔案。 - 提交遷移檔案到 Git,確保版本控制。 4. **版本控制**: - 將 `supabase/` 資料夾(包含遷移和設定)提交到 Git。 - 這允許追蹤資料庫變更,並與應用程式代碼一起管理。 通過這種方式,Supabase 結合 GUI 的便利性和 CLI 的版本控制,實現高效的全端開發。 ### schema #### RLS - **RLS 定義**:Row Level Security 是 PostgreSQL 的資料庫層級功能,通過 `CREATE POLICY` 語句實現。 - **資料庫層級安全性**:直接在資料庫引擎中強制執行存取控制,根據使用者上下文(如已驗證使用者 ID)過濾資料列,防止未授權存取。 - **後端整合**:Supabase 後端(包括 API 層)整合 PostgreSQL,驗證 JWT 權杖並應用 RLS 政策。後端涵蓋資料庫、Auth 服務和 API 閘道,安全性整體強制執行。 - **為何不只用後端程式碼**:RLS 提供效率和一致性,確保即使直接資料庫查詢(例如 SQL 編輯器)也能安全,減少繞過應用層檢查的風險。 - **常見做法**:這是現代 BaaS 平台的標準安全方法。若偏好應用層控制,可在客戶端或伺服器程式碼中新增中介軟體,但 RLS 提供堅實基礎。 - **參考**:[Supabase RLS Docs](https://supabase.com/docs/guides/auth/row-level-security)。 ### API-edge function 一般希望在 BaaS 上不用寫 backend code,但有時候還是需要一些功能,需要回歸 backend development。 edge function 就是 supabase 裡回到 backend function define 的方法 。此時 supbase 像是 backend framework 而非純 BaaS。 Supabase Edge Functions 是基於 Deno 的無伺服器函數,style in serverless + typescript。 這些函數適合處理需要自定義邏輯但不適合在前端執行的任務,例如整合第三方 API 或執行複雜計算。 {%preview https://supabase.com/docs/reference/self-hosting-functions/introduction%} `supabase functions serve` is blocking for backend serving. local supabase will **not** show server. ### API-RMDBS * Supabase 提供自動生成的 REST API,基於您的 PostgreSQL 資料庫 schema。您可以直接從前端查詢、插入、更新和刪除資料,而無需編寫自定義後端。 * 產生 view 所能做的簡化效益更大了。 * 也能直接從前端下 SQL 查詢。 * 支援 RPC(遠程過程調用)以執行自定義的 postgres SQL 函數和存儲過程。 * 以上都是從前端直接作用於資料庫。 ### API-realtime API * Supabase 提供即時功能,允許前端應用訂閱資料庫變更並接收即時更新。 * 這通過 WebSocket 實現,避免了輪詢(polling)。 * 您可以在 Supabase 儀表板中為表格啟用即時功能,然後使用 SDK 訂閱變更事件,如插入、更新或刪除。 * 這對於聊天應用、協作工具或任何需要即時同步的場景非常有用。 supabase realtime [有 3 種](https://supabase.com/docs/guides/realtime/getting_started#choose-the-right-feature), broadcast, postgres change, Presence。其中 95% 情況都該用 broadcat。 Presence for share state. "postgres change" 發現對 `supabase db diff` 有問題,無法從 schema 轉到 migration。必須直接寫在 migration file。 {%preview https://supabase.com/docs/guides/realtime/subscribing-to-database-changes#using-broadcast %} ### API-object storage service API direct save local file 很難保證環境,並且不易擴展。所以要 OSS。 MinIO 可以 self-hosted,但 supabase 直接內建, 讓採用OSS 的阻力小很多。 ### hosting supabase hosting ### deploy * 支援靜態網站託管(frontend) 和邊緣函數(backend) 部署 ## user story ### n+1 problem - **問題原因**:Supabase 後端端點基於資料庫 schema 自動生成(通過 REST API),但需優化聯結資料以避免 N+1 問題(多個查詢而非一個有效聯結)。 - **解決方案**: - **查詢中使用聯結**:使用 SDK 的 `select()` 進行巢狀關聯查詢,如 `users(*, posts(*))`。 - **建立資料庫視圖**:定義預聯結資料的視圖,然後查詢視圖。 - **使用 RPC 函數**:編寫存儲過程處理複雜聯結,並通過 `supabase.rpc()` 調用。 - **GraphQL(若啟用)**:使用 Supabase 的 GraphQL API 進行有效巢狀查詢。 - **效益**:將查詢減少到 1,提升效能。可通過 Supabase 查詢日誌監控。 - **查詢聯結 vs. 資料庫視圖比較**: - **查詢聯結**: - **運作方式**:使用 SDK 的 `select()` 進行即時聯結。 - **優點**:靈活、動態查詢;無需 schema 變更;易於程式碼修改;支援即時訂閱。 - **缺點**:過度使用可能導致複雜查詢;效能取決於查詢優化;不可跨應用重用。 - **效能**:適合臨時聯結,但大型資料集需索引。 - **資料庫視圖**: - **運作方式**:建立預定義視圖,如 `CREATE VIEW user_posts AS SELECT ... FROM users JOIN posts ...`,然後查詢視圖。 - **優點**:可跨查詢/應用重用;預優化;簡化程式碼;適合複雜靜態聯結。 - **缺點**:需遷移;動態變更較不靈活;視圖若資料變更頻繁可能過時。 - **效能**:適合重複、可預測查詢;可索引以提升速度。 - **選擇時機**: - 選擇查詢聯結:動態、一次性查詢;原型開發;避免資料庫變更;即時更新關鍵。 - 選擇資料庫視圖:靜態、常用聯結;簡化前端程式碼;大型應用效能優先。 - **建議**:從聯結開始,若查詢重複則遷移到視圖。 ### authentication and security supabase 作為 BaaS,能做的驗證基礎都是依賴 [RLS](https://hackmd.io/1_vBidOWR6-hBmdvK_jfzA#RLS) 達成的,而非在 backend 層才處理。 也唯有這樣才能處理 N+1 problem。 - **驗證流程**: - **使用者註冊/登入**:Supabase 使用 Auth 服務(基於 GoTrue)處理註冊、登入和密碼管理。支援方法如 email/密碼、OAuth(Google、GitHub)、魔術連結。 - **權杖生成**:驗證成功後,Supabase 發行包含使用者聲明(ID、角色)的 JWT。權杖存儲在客戶端(本地儲存),並隨 API 請求發送。 - **限制 API 存取**: - **JWT 授權**:API(資料庫查詢)需有效 JWT 在 `Authorization` 標頭。Supabase 每請求驗證權杖,確保使用者已驗證。 - **Row Level Security (RLS)**:資料庫存取強制 RLS 政策,根據使用者 ID 或角色限制資料可見性和操作。 - **API 金鑰和角色**: - **Anon Key**:公開、未驗證存取(限公開表格)。 - **Service Role Key**:伺服器端完整存取(用於伺服器環境,非客戶端)。 - 可定義自訂角色以授予細粒度權限。 - **即時和 Edge Functions**:驗證類似強制,需 JWT 進行訂閱或函數調用。 - **實作步驟**: 1. 在 Supabase 儀表板啟用表格的 RLS。 2. 建立政策,如 `CREATE POLICY user_data ON table FOR SELECT USING (auth.uid() = user_id);`。 3. 在客戶端程式碼初始化客戶端,使用專案 URL 和 anon key,然後使用 `supabase.auth.signIn()` 登入。 4. SDK 自動附加 JWT 至 API 呼叫。 - **前端連接**: - 前端通過 Supabase SDK(`@supabase/supabase-js`)直接連接後端。SDK 處理驗證、API 呼叫和即時訂閱,後端強制安全性如 RLS。 - **無需自訂後端**:Supabase 提供後端。 - **範例用法**: ```javascript import { createClient } from '@supabase/supabase-js'; const supabase = createClient('your-project-url', 'your-anon-key'); const { user, error } = await supabase.auth.signIn({ email, password }); const { data } = await supabase.from('table').select('*'); ``` - 保持前端簡單且安全。若需整合自訂後端,可代理請求,但 Supabase 設計為直接前端存取。 the frontend connects directly to the backend via the Supabase SDK (e.g., `@supabase/supabase-js` for JavaScript). The SDK handles authentication, API calls, and real-time subscriptions, while the backend (database, auth, and APIs) enforces security like RLS. example ```javascript // filepath: src/supabaseClient.js import { createClient } from '@supabase/supabase-js'; const supabase = createClient('your-project-url', 'your-anon-key'); // Authenticate user const { user, error } = await supabase.auth.signIn({ email, password }); // Query data (RLS enforced) const { data } = await supabase.from('table').select('*'); ``` This keeps the frontend simple and secure. If you need to integrate with a custom backend, you can proxy requests, but Supabase is designed for direct frontend access. For more details, check the [Supabase Quickstart](https://supabase.com/docs/guides/getting-started). ### users management app 一般的情況下,supabase 不預期 單一user 可以掌控其它人的帳號。 {%preview https://supabase.com/docs/guides/getting-started/tutorials/with-nextjs?queryGroups=database-method&database-method=sql %} 自己的基本資料,有 jwt,都是可以自己拿到的。 [getUser](https://supabase.com/docs/reference/javascript/auth-getuser) 其實連額外的 user 資料都要自己存。因為要通過 supabase authentication 有相當的難度。所以想要查閱的 profile 就要app自己存。 [Accessing user data via API](https://supabase.com/docs/guides/auth/managing-user-data?queryGroups=language&language=js#accessing-user-data-via-api) #### manager management app 但在 ERP 下,大部份的軟體都需要直接控制帳號。 ``` I am creating ERP through supabase. I want manager able to manage users account. How to? ``` manager management 想做的事,說到底,就是繞過 auth user 驗證。 supabase 大方向上,讓 authentication 在 server side 做是必要的。特別是 user creation 不可能避開。 下一步的細節就是 實現的方法。 Give managers a manager-only server/Edge endpoint (or Supabase Function) that verifies the caller is a manager, then uses the service-role client to call the Admin API (create/delete/update an auth user) and to insert/update the profiles table (RLS + policies control who can change what). [ Create new User from another account ](https://www.reddit.com/r/Supabase/comments/11l7zba/comment/jbbpgwi/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button) {%preview https://github.com/usebasejump/basejump %} project dead。似乎也沒有其它的repo for supabase。 ##### RUD create User 是確定了,那其它的功能呢? RUD? 雖然 profile 預期提供 額外資料。但 email 之類 user 本來就提供的資料,在範例裡就沒有寫。 如果用副本了話,就會有 copy 問題。如果profile 有 email ,且 profile email 是 designed read only,則可以用 trigger 做到自動更新。 * 但從 profile email 用 trigger 更新 auth user email 是可能的嗎? * > if it is possible that profile email update trigger auth user email update? * AI 說可以。但實際上,網路上找不到人問這個題目。 * 其實如果不能 create User,大概率也是不能 create trigger to modify auth user。否則就能用 trigger 做到 creatre auth user。 所以要嘛 readonly email on profile,(也只能簡化 CRUD 的 read,其它都要write access)。否則就用 edge function 到底。 如果需要 search user by email,也可以做 read only copy。 我覺得 profile read only attribute 的收效實在太低。直接用 edge function 才是 和 auth user 打交道的好方式。 或是直接要求 user 填入 service role key。直接擴展權限。或是說把 使用者端口 和 控制端 統一在同一個介面。 當然對 ERP 系統來說怎麼做比較好就用那個。一般是法1。 後端 service role `updateUserById`, `listUsers`, `getUserById`... 要做到 searching auth user ,就是先用 profile text search。再用 `getUserById` 取得 user 實際資料。 * [ It is NOT possible to create a trigger on the Auth users table ](https://www.reddit.com/r/Supabase/comments/1h7bnra/comment/m0k5ayr/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button) * > You can create auth triggers in SQL, but not through the UI—and this is intentional. We received numerous support tickets from users who inadvertently broke their auth schema by creating triggers. To prevent these issues, we’ve disabled the option in the UI, reserving it for advanced use cases where users understand the risks and nuances. This helps reduce cases where triggers become a "footgun." * [On Supabase free tier, you cannot create triggers directly on the table because you don’t have ownership or superuser privileges. This is a platform restriction, not a misconfiguration.](https://github.com/orgs/supabase/discussions/38887#discussioncomment-14463359) * Recommended Alternative: Use **Supabase Edge Functions** or Client-side Logic * Automate with Supabase Auth Hooks (if available) * Supabase now supports “Auth Hooks” (beta/paid plans) for post-signup actions. On free tier, this may not be available, so fallback to client/server logic. ### subscribe and realtime update 首先要理解的是 web dev 還缺乏一個 完整且規範化的 subscribe spec (web-grpc, graphql 或許算),或至少沒有成為事實標準者。supabase realtime api 就是戰國時代裡的一個選擇。但只要這個選擇夠好用穩定,那根本也不太需要其他選擇,就像每天都吃自助餐一樣。 - **運作方式**: - **啟用即時**:在 Supabase 儀表板為表格啟用即時(Database > Tables)。 - **程式碼訂閱**:使用 SDK 訂閱變更。基於事件驅動,非輪詢。 - **優點**:高效、低延遲更新;支援聯結和過濾。 - **範例實作**: ```javascript import { createClient } from '@supabase/supabase-js'; const supabase = createClient('your-project-url', 'your-anon-key'); const channel = supabase .channel('table_changes') .on('postgres_changes', { event: '*', // 監聽 INSERT, UPDATE, DELETE schema: 'public', table: 'your_table' }, (payload) => { console.log('Change received:', payload); // 更新前端狀態 }) .subscribe(); // 取消訂閱: channel.unsubscribe(); ``` - 簡單整合 Supabase 設定。進階用法參考 [Supabase Realtime Docs](https://supabase.com/docs/guides/realtime)。 ## API 選擇 是想回到 client app links to own DB 的情況。 大前端可能是更符合這種情況。所以RLS 有足夠安全性限制的東西就直接放前端就好。其實就只有 3rd 服務需要先用backend 包一層。 業務邏輯,在前端一樣可以先封裝過。不影響架構。 平衡型前後端,可能有後端先 unit test 的好處? 要看supabase 的檢查機制做多好,如果產生 API 時就有完美檢查了話就不需要。 ## supabase auth [Auth](https://supabase.com/docs/guides/auth) focus 在 email, oauth(google)。 角色分 guest, worker, manager 權限。 讓 manager 可以在獨立業面直接完成 worker 建立。 [ 第十七關 - 魔力波長:Supabase Auth ](https://ithelp.ithome.com.tw/articles/10391283), [ 第二十二關 - 來企排隊:Supabase 快速建立信箱註冊與登入 ](https://ithelp.ithome.com.tw/articles/10393609) 看起來這個作者的做法是額外建立 public user schema,然後加上 auth.id 的RLS 限制。 p.s. 這個user 也是以 SQL source code 為開發主體。 [ 第二十四關 - 來企排隊: Supabase 快速建立商家列表和預約功能 ](https://ithelp.ithome.com.tw/articles/10394426) > owner_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, 可見 商家也是綁到 auth user。 [examples/user-management/flutter-user-management](https://github.com/supabase/supabase/tree/master/examples/user-management/flutter-user-management) 官方 example 也是如此,所以這是標準做法。 ### RBAC [Custom Claims & Role-based Access Control (RBAC)](https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac) 但由於RLS/RBAC,seeding 無法工作。可能需要用 meta-progarmming 的方式 產生 sql。 [Seeding auth.users](https://github.com/orgs/supabase/discussions/35391) ## email auth [How to view emails on the local dev](https://www.answeroverflow.com/m/1398069064517025833) `Mailpit` 是 關鍵。 [Local development with Mailpit](https://supabase.com/docs/guides/auth/passwords#local-development-with-mailpit)