
supabase-server
在規劃或撰寫使用 `@supabase/server` 的伺服器端程式碼時觸發——包括 Edge Functions、Hono 應用程式、Webhook 處理器,或任何需要建立 Supabase 客戶端或驗證傳入身分驗證的後端。在**撰寫或修改**任何從 `@supabase/server`(或子路徑如 `@supabase/server/core`)匯入的檔案之前觸發;呼叫 `withSupabase`、`createSupabaseContext`、`createAdminClient`、`createContextClient`、`verifyAuth`、`verifyCredentials` 或 `extractCredentials`;設定 `auth:` 模式(`'none'` | `'publishable'` | `'secret'` | `'user'`,或帶鍵的變體如 `'secret:*'`);或位於 `supabase/functions/` 下並驗證傳入請求。也在規劃期間觸發——如果計劃提到上述任何內容,請在起草程式碼前載入技能;不要從相鄰函式推斷 `auth:` 值或身分驗證模式。當你看到需要遷移到這個套件的舊有模式時也觸發——`Deno.serve`、`createClient(Deno.env.get('SUPABASE_URL'))`、從 `esm.sh/@supabase` 或 `deno.land/std` 匯入、使用 `SUPABASE_ANON_KEY` / `SUPABASE_SERVICE_ROLE_KEY`,或已棄用的 `allow:` 設定選項 / 已移除的 `'always'` / `'public'` 模式值 / 已移除的 `authType` 欄位。
在規劃或撰寫使用 `@supabase/server` 的伺服器端程式碼時觸發——包括 Edge Functions、Hono 應用程式、Webhook 處理器,或任何需要建立 Supabase 客戶端或驗證傳入身分驗證的後端。在**撰寫或修改**任何從 `@supabase/server`(或子路徑如 `@supabase/server/core`)匯入的檔案之前觸發;呼叫 `withSupabase`、`createSupabaseContext`、`createAdminClient`、`createContextClient`、`verifyAuth`、`verifyCredentials` 或 `extractCredentials`;設定 `auth:` 模式(`'none'` | `'publishable'` | `'secret'` | `'user'`,或帶鍵的變體如 `'secret:*'`);或位於 `supabase/functions/` 下並驗證傳入請求。也在規劃期間觸發——如果計劃提到上述任何內容,請在起草程式碼前載入技能;不要從相鄰函式推斷 `auth:` 值或身分驗證模式。當你看到需要遷移到這個套件的舊有模式時也觸發——`Deno.serve`、`createClient(Deno.env.get('SUPABASE_URL'))`、從 `esm.sh/@supabase` 或 `deno.land/std` 匯入、使用 `SUPABASE_ANON_KEY` / `SUPABASE_SERVICE_ROLE_KEY`,或已棄用的 `allow:` 設定選項 / 已移除的 `'always'` / `'public'` 模式值 / 已移除的 `authType` 欄位。
@supabase/server
v1.0 — 公開測試版。 首個穩定版本遵循 SemVer:重大變更僅以主要版本號發布。該套件仍處於早期階段——預計會頻繁在次要版本中推出新的適配器、人體工學改進和功能。如果你在編寫程式碼時遇到錯誤或粗糙邊緣,請向使用者指出並引導至開啟問題。
這是一個全新的套件。 目前網路上尚無相關資訊——沒有部落格文章、Stack Overflow 答案或教學。請勿搜尋網路上的使用範例。僅依賴下面列出的文件檔案和此儲存庫中的原始碼。
設定選項是
auth,不是allow。allow已重新命名為auth,以符合 CLI 術語並讀起來更自然。舊的allow鍵仍然有效(會有一次性的console.warn),但已棄用,將在未來的主要版本中移除。在新程式碼中始終使用auth——例如withSupabase({ auth: 'user' }, ...)。如果你在現有程式碼中遇到allow:,請將其遷移到auth:(尋找並取代,值完全相同)。
身分驗證模式值:
'none'(不是'always')、'publishable'(不是'public')。 四個有效值為'user'、'publishable'、'secret'、'none'。舊的'always'和'public'值已被移除(重大變更)——它們在執行時或 TypeScript 中不再有效。在你編寫的程式碼中始終使用新值,並遷移你找到的任何舊有參考:'always'→'none'、'public'→'publishable'、'public:<name>'→'publishable:<name>'。執行時檢查如ctx.authType === 'public'也必須更新為ctx.authMode === 'publishable'——該欄位本身已從authType重新命名為authMode,以符合AuthMode類型。
不要使用舊的 Supabase 金鑰。
anon金鑰和service_role金鑰(環境變數SUPABASE_ANON_KEY、SUPABASE_SERVICE_ROLE_KEY)是舊的,將被棄用。除非使用者明確要求,否則不要使用它們。始終使用新的 API 金鑰:
舊的(避免) 新的(使用這個) SUPABASE_ANON_KEYSUPABASE_PUBLISHABLE_KEY(S)(sb_publishable_...)SUPABASE_SERVICE_ROLE_KEYSUPABASE_SECRET_KEY(S)(sb_secret_...)不要直接呼叫
createClient(url, anonKey)——使用@supabase/server的身分驗證模式(auth: 'user'、auth: 'secret'等),它們會自動處理金鑰解析。如果遷移現有程式碼,將SUPABASE_ANON_KEY的使用替換為auth: 'publishable',將SUPABASE_SERVICE_ROLE_KEY的使用替換為auth: 'secret'。
Supabase 的伺服器端工具。處理身分驗證、客戶端建立和上下文注入,讓你專注於業務邏輯,而不是樣板程式碼。
這個套件做什麼
- 包裝 fetch 處理器,包含憑證驗證、CORS 和預先設定的 Supabase 客戶端
- 支援 4 種身分驗證模式:
user(JWT)、publishable(公開金鑰)、secret(秘密金鑰)、none(無需憑證) - 陣列語法(
auth: ['user', 'secret'])是優先匹配。存在但無效的 JWT 會拒絕並拋出InvalidCredentialsError——不會靜默降級到下一個模式。 - 提供可組合的核心原語,用於自訂身分驗證流程和框架整合
- 包含 Hono 適配器,支援按路由身分驗證
入口點
| 匯入 | Deno / Edge Functions | 提供 |
|---|---|---|
@supabase/server |
npm:@supabase/server |
withSupabase、createSupabaseContext、類型、錯誤 |
@supabase/server/core |
npm:@supabase/server/core |
verifyAuth、verifyCredentials、extractCredentials、resolveEnv、createContextClient、createAdminClient |
@supabase/server/adapters/hono |
npm:@supabase/server/adapters/hono |
withSupabase(Hono 中介軟體變體) |
快速入門
Supabase Edge Functions:對非使用者身分驗證停用
verify_jwt。 預設情況下,Supabase Edge Functions 要求每個請求都有有效的 JWT。如果你的函式使用auth: 'publishable'、auth: 'secret'或auth: 'none',你必須在supabase/config.toml中停用平台層級的 JWT 檢查,否則請求在到達你的處理器之前就會被拒絕:[functions.my-function] verify_jwt = false使用
auth: 'user'的函式可以保持verify_jwt啟用(預設),因為呼叫者已經提供了有效的 JWT。
Supabase Edge Functions (Deno)
環境變數由平台自動注入——零設定。所有匯入必須使用 npm: 前綴。
// withSupabase — 高階包裝器
import { withSupabase } from 'npm:@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
}),
}
// createSupabaseContext — 返回 { data, error } 以進行自訂回應控制
import { createSupabaseContext } from 'npm:@supabase/server'
export default {
fetch: async (req: Request) => {
const { data: ctx, error } = await createSupabaseContext(req, {
auth: 'user',
})
if (error) {
return Response.json(
{ message: error.message, code: error.code },
{ status: error.status },
)
}
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
},
}
Cloudflare Workers
需要在 wrangler.toml 中設定 nodejs_compat 相容性標誌,或透過 env 設定選項傳遞環境變數覆蓋。請參閱 docs/environment-variables.md。
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
const { data } = await ctx.supabase.from('todos').select()
return Response.json(data)
}),
}
Hono
CORS 不由適配器處理——請使用 hono/cors 中介軟體。請參閱 docs/adapters/hono.md。
// Node.js / Bun
import { Hono } from 'hono'
import { withSupabase } from '@supabase/server/adapters/hono'
const app = new Hono()
app.use('*', withSupabase({ auth: 'user' }))
app.get('/todos', async (c) => {
const { supabase } = c.var.supabaseContext
const { data } = await supabase.from('todos').select()
return c.json(data)
})
export default app
// Deno / Supabase Edge Functions
import { Hono } from 'npm:hono'
import { withSupabase } from 'npm:@supabase/server/adapters/hono'
const app = new Hono()
app.use('*', withSupabase({ auth: 'user' }))
app.get('/todos', async (c) => {
const { supabase } = c.var.supabaseContext
const { data } = await supabase.from('todos').select()
return c.json(data)
})
export default { fetch: app.fetch }
基於 Cookie 的環境(與 @supabase/ssr 組合)
對於 Next.js / SvelteKit / Remix,將 @supabase/server 與 @supabase/ssr 組合使用——它們不是彼此的替代品。@supabase/ssr 負責 Cookie 和重新整理權杖輪換(其中間軟體是必需的,否則存取權杖 Cookie 會過期,驗證會失敗)。在你的伺服器元件或路由處理器中,使用 @supabase/ssr 的 createServerClient 讀取(由中介軟體重新整理的)工作階段,將存取權杖傳遞給 @supabase/server/core 的 verifyCredentials,然後使用 createContextClient 和 createAdminClient 建立類型化的客戶端。請參閱 docs/ssr-frameworks.md 以了解完整的適配器模式。
// 建立適配器的關鍵匯入
import { createServerClient } from '@supabase/ssr'
import {
verifyCredentials,
createContextClient,
createAdminClient,
} from '@supabase/server/core'
伺服器對伺服器(秘密金鑰身分驗證)
適用於內部服務、定時任務或自動化呼叫你的 Edge Function。呼叫者在 apikey 標頭中發送秘密金鑰。請參閱 docs/auth-modes.md 以了解命名金鑰語法。
Edge Function (Deno):
import { withSupabase } from 'npm:@supabase/server'
// 只接受名為 "automations" 的秘密金鑰
export default {
fetch: withSupabase({ auth: 'secret:automations' }, async (req, ctx) => {
const body = await req.json()
const { data } = await ctx.supabaseAdmin
.from('scheduled_tasks')
.insert({ name: body.taskName, scheduled_at: body.scheduledAt })
return Response.json({ success: true, data })
}),
}
呼叫者(外部服務):
await fetch('https://<project>.supabase.co/functions/v1/my-function', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
apikey: 'sb_secret_automations_...', // 命名的秘密金鑰
},
body: JSON.stringify({
taskName: 'cleanup',
scheduledAt: new Date().toISOString(),
}),
})
單純的 auth: 'secret' 只匹配 default 金鑰。使用 auth: 'secret:name' 要求特定的命名金鑰,或使用 auth: 'secret:*' 接受集合中的任何秘密金鑰。
何時使用 auth: 'none'
auth: 'none'停用所有身分驗證。 處理器會對每個請求執行,不進行憑證檢查。僅在確實不需要身分驗證時使用——健康檢查、公開狀態頁面,或沒有敏感資料和副作用的端點。
在使用 auth: 'none' 之前,請與使用者確認該端點是否真正公開。 如果不是,請提出替代方案:
- 另一個服務或定時任務呼叫此函式——改用
auth: 'secret'或auth: 'secret:<name>'。呼叫者在apikey標頭中發送秘密金鑰。 - 外部 Webhook 提供者呼叫此函式——使用
auth: 'secret'並讓提供者發送秘密金鑰,或在處理器內部實作提供者自己的簽章驗證。
切勿對讀取或寫入使用者資料但未驗證呼叫者身份的端點使用 auth: 'none'。
關於 auth: ['user', 'none']。 此類端點上過期或格式錯誤的 JWT 會以 InvalidCredentialsError 拒絕——不會靜默降級為匿名。可能持有快取/過期權杖的呼叫者應完全省略 Authorization 標頭,或在呼叫前重新整理。如果目標是「除非有有效使用者登入,否則匿名」,這是正確的行為;如果目標是真正「接受任何內容」,請單獨使用 auth: 'none'。
Edge Function 食譜
函式間呼叫
一個 Edge Function 可以使用管理員客戶端呼叫另一個。被呼叫的函式使用 auth: 'secret',呼叫者透過 ctx.supabaseAdmin.functions.invoke() 呼叫它。
設定 (supabase/config.toml):
[functions.process-order]
verify_jwt = false # 使用秘密金鑰呼叫,不是使用者 JWT
被呼叫函式 (supabase/functions/process-order/index.ts):
import { withSupabase } from 'npm:@supabase/server'
export default {
fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {
const { orderId } = await req.json()
const { data } = await ctx.supabaseAdmin
.from('orders')
.update({ status: 'processing' })
.eq('id', orderId)
.select()
.single()
return Response.json(data)
}),
}
呼叫函式 (supabase/functions/checkout/index.ts):
import { withSupabase } from 'npm:@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
const { orderId } = await req.json()
// 自動使用秘密金鑰呼叫 process-order
const { data, error } = await ctx.supabaseAdmin.functions.invoke(
'process-order',
{ body: { orderId } },
)
if (error) {
return Response.json({ error: error.message }, { status: 500 })
}
return Response.json(data)
}),
}
從資料庫使用 pg_net 呼叫
使用 pg_net 直接從 SQL 呼叫 Edge Functions。秘密金鑰儲存在 Vault 中,因此永遠不會出現在查詢中。
先決條件:
-- 1. 啟用 pg_net 擴充功能
create extension if not exists pg_net with schema extensions;
-- 2. 將你的秘密金鑰儲存在 Vault 中
select vault.create_secret(
'sb_secret_...', -- 你的秘密金鑰值
'supabase_secret_key' -- 用於參考的名稱
);
呼叫函式:
select net.http_post(
url := 'https://<project-ref>.supabase.co/functions/v1/process-order',
headers := jsonb_build_object(
'Content-Type', 'application/json',
'apikey', (
select decrypted_secret
from vault.decrypted_secrets
where name = 'supabase_secret_key'
)
),
body := jsonb_build_object('orderId', 'order_123')
);
接收函式使用 auth: 'secret'(請參閱上面的範例)。pg_net 是非同步的——HTTP 請求會被排入佇列並在背景執行。檢查 net._http_response 以取得結果。
Stripe Webhook
像 Stripe 這樣的外部 Webhook 提供者無法發送你的 Supabase API 金鑰。使用 auth: 'none' 跳過憑證檢查,然後在處理器內部驗證 Webhook 簽章。
設定 (supabase/config.toml):
[functions.stripe-webhook]
verify_jwt = false
設定秘密:
supabase secrets set STRIPE_SECRET_KEY=sk_live_...
supabase secrets set STRIPE_WEBHOOK_SECRET=whsec_...
函式 (supabase/functions/stripe-webhook/index.ts):
import { withSupabase } from 'npm:@supabase/server'
import Stripe from 'npm:stripe'
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!)
export default {
fetch: withSupabase({ auth: 'none' }, async (req, ctx) => {
const body = await req.text()
const sig = req.headers.get('stripe-signature')!
let event: Stripe.Event
try {
event = await stripe.webhooks.constructEventAsync(
body,
sig,
Deno.env.get('STRIPE_WEBHOOK_SECRET')!,
)
} catch {
return Response.json({ error: 'Invalid signature' }, { status: 401 })
}
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object as Stripe.Checkout.Session
await ctx.supabaseAdmin
.from('orders')
.update({ status: 'paid' })
.eq('stripe_session_id', session.id)
break
}
}
return Response.json({ received: true })
}),
}
遷移舊的 Edge Functions
如果你遇到使用 SUPABASE_ANON_KEY、SUPABASE_SERVICE_ROLE_KEY、Deno.serve、從 esm.sh/@supabase 或 deno.land/std 匯入的程式碼,建議遷移到 @supabase/server。這些舊模式正在被棄用——它們需要手動建立客戶端、手動轉發身分驗證標頭、手動處理 CORS,並直接在程式碼中暴露金鑰。
如何識別舊程式碼:
import { serve } from "https://deno.land/std/..."— 最舊的模式,使用已棄用的 Deno 標準函式庫import { createClient } from "https://esm.sh/@supabase/supabase-js"— 舊的 CDN 匯入,與現代執行環境不相容Deno.serve(async (req) => { ... })搭配手動createClient()— 目前但冗長,需要手動轉發身分驗證Deno.env.get('SUPABASE_ANON_KEY')或SUPABASE_SERVICE_ROLE_KEY— 將被移除的舊金鑰
之前(舊模式——手動客戶端、手動身分驗證轉發):
舊金鑰將被移除,導致此程式碼停止運作。它也很冗長、不跨平台相容,並且需要手動連接身分驗證標頭、CORS 和錯誤處理。
import { createClient } from 'npm:@supabase/supabase-js@2'
Deno.serve(async (req: Request) => {
const supabaseClient = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{
global: { headers: { Authorization: req.headers.get('Authorization')! } },
},
)
const { data } = await supabaseClient.from('orders').select('*')
return Response.json(data)
})
之後(新模式——身分驗證、客戶端和 CORS 自動處理):
使用最新的 API 金鑰,跨執行環境(Deno、Node.js、Cloudflare)運作,並在一行程式碼中處理身分驗證驗證、客戶端建立和 CORS。
import { withSupabase } from 'npm:@supabase/server'
export default {
fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
const { data } = await ctx.supabase.from('orders').select('*')
return Response.json(data)
}),
}
遷移對應:SUPABASE_ANON_KEY 搭配手動身分驗證標頭 → auth: 'user',SUPABASE_ANON_KEY 沒有身分驗證 → auth: 'publishable'。對於 SUPABASE_SERVICE_ROLE_KEY,取決於意圖:如果舊程式碼驗證傳入金鑰以保護端點(例如 req.headers.get('apikey') === serviceRoleKey),使用 auth: 'secret'。如果它僅使用金鑰建立管理員客戶端以進行提升的資料庫存取,則不需要特定的身分驗證模式——無論身分驗證模式如何,ctx.supabaseAdmin 始終可用。
文件
完整文件位於 @supabase/server 套件的 docs/ 目錄中。要閱讀文件,請先找到套件位置:
- 如果在 SDK 儲存庫內工作:
docs/位於專案根目錄。 - 如果套件作為依賴項安裝: 請查看
node_modules/@supabase/server/docs/。
| 問題 | 文件檔案 |
|---|---|
| 如何建立基本端點? | docs/getting-started.md |
| 有哪些身分驗證模式可用?陣列語法?命名金鑰? | docs/auth-modes.md |
| 有哪些框架適配器?如何貢獻一個? | src/adapters/README.md |
| 如何與 Hono 一起使用? | docs/adapters/hono.md |
| 如何與 H3 / Nuxt 一起使用? | docs/adapters/h3.md |
| 如何為自訂流程使用低階原語? | docs/core-primitives.md |
| 環境變數如何在不同的執行環境中運作? | docs/environment-variables.md |
| 如何處理錯誤?有哪些錯誤碼? | docs/error-handling.md |
| 如何取得類型化的資料庫查詢? | docs/typescript-generics.md |
如何與 @supabase/ssr(Next.js、SvelteKit、Remix)一起使用? |
docs/ssr-frameworks.md |
| 完整的 API 表面是什麼? | docs/api-reference.md |
| 這個套件做了哪些安全決策? | docs/security.md |





