Edge Functions
Deno で動く Supabase 内のサーバ側関数。 外部 API 呼び出し、認証検証後の処理、Webhook 受信、Cron 用途まで。
特徴
- Deno + TypeScriptがそのまま動く
- 世界中のエッジで実行(Cloudflare Workers ベース)
- Supabase の URL / キーが環境変数として注入される
- HTTP リクエストを受け取り Response を返す(Web 標準)
- npm モジュールも使える(jsr / npm import 対応)
新規作成
supabase functions new hello
# supabase/functions/hello/index.ts が作られる
最小例
// supabase/functions/hello/index.ts
Deno.serve(async (req) => {
const { name } = await req.json()
return new Response(JSON.stringify({ message: `Hello, ${name}!` }), {
headers: { "Content-Type": "application/json" },
})
})
ローカルで動かす
supabase functions serve hello
# 別ターミナルで
curl -X POST http://localhost:54321/functions/v1/hello \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SUPABASE_ANON_KEY" \
-d '{"name":"world"}'
デプロイ
supabase functions deploy hello
# 認証無しで呼べるようにする
supabase functions deploy hello --no-verify-jwt
呼び出し
// supabase-js から
const { data, error } = await supabase.functions.invoke("hello", {
body: { name: "Alice" },
})
// 直 fetch
await fetch(`${URL}/functions/v1/hello`, {
method: "POST",
headers: {
Authorization: `Bearer ${session.access_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ name: "Alice" }),
})
環境変数
Supabase が自動で注入する変数:
SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEYSUPABASE_DB_URL(Postgres 接続)
独自シークレット
supabase secrets set OPENAI_API_KEY=sk-...
supabase secrets list
supabase secrets unset OPENAI_API_KEY
# .env を一括投入
supabase secrets set --env-file ./supabase/.env
const apiKey = Deno.env.get("OPENAI_API_KEY")!
DB に接続
supabase-js(推奨)
import { createClient } from "jsr:@supabase/supabase-js@2"
Deno.serve(async (req) => {
// 1) ユーザ権限で(RLS 適用)
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_ANON_KEY")!,
{
global: { headers: { Authorization: req.headers.get("Authorization")! } },
}
)
const { data, error } = await supabase.from("posts").select()
return new Response(JSON.stringify({ data, error }))
})
service_role で(管理者として)
const admin = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
)
// RLS をバイパスして全件操作できる
認証ユーザの取得
Deno.serve(async (req) => {
const supabase = createClient(URL, ANON_KEY, {
global: { headers: { Authorization: req.headers.get("Authorization")! } },
})
const { data: { user } } = await supabase.auth.getUser()
if (!user) return new Response("Unauthorized", { status: 401 })
// user.id で何かする
})
CORS 対応
const cors = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
"Access-Control-Allow-Methods": "POST, GET, OPTIONS",
}
Deno.serve(async (req) => {
if (req.method === "OPTIONS") return new Response("ok", { headers: cors })
// 処理...
return new Response(JSON.stringify(result), {
headers: { ...cors, "Content-Type": "application/json" },
})
})
典型ユースケース
1. 外部 API 呼び出し(API キーを隠す)
Deno.serve(async (req) => {
const { prompt } = await req.json()
const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": Deno.env.get("ANTHROPIC_API_KEY")!,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
}),
})
return new Response(res.body, { headers: { "Content-Type": "application/json" } })
})
2. Webhook 受信(Stripe 等)
import Stripe from "https://esm.sh/stripe@14"
const stripe = new Stripe(Deno.env.get("STRIPE_SECRET")!)
Deno.serve(async (req) => {
const sig = req.headers.get("stripe-signature")!
const body = await req.text()
const event = await stripe.webhooks.constructEventAsync(
body, sig, Deno.env.get("STRIPE_WEBHOOK_SECRET")!
)
if (event.type === "checkout.session.completed") {
// DB 更新
}
return new Response("ok")
})
3. Auth Hook(ユーザ作成時のフック)
Supabase の Auth Hook 機能で、サインアップ時に自動で関数を呼べる:
// HTTP Hook として登録
Deno.serve(async (req) => {
const { user_id, email, claims } = await req.json()
// Slack 通知 / DB に追加情報書き込み 等
return new Response(JSON.stringify({ claims }), {
headers: { "Content-Type": "application/json" },
})
})
4. Cron / 定期ジョブ
Supabase 内の pg_cron 拡張で、定期的に Edge Function を呼び出せる:
select cron.schedule(
'daily-report',
'0 0 * * *', -- 毎日 0 時
$$
select net.http_post(
'https://xxxx.supabase.co/functions/v1/daily-report',
'{}'::jsonb,
'{"Authorization": "Bearer SERVICE_KEY"}'::jsonb
);
$$
);
5. ストリーミング(LLM 等)
Deno.serve(async () => {
const stream = new ReadableStream({
async start(controller) {
for (let i = 0; i < 10; i++) {
controller.enqueue(new TextEncoder().encode(`chunk ${i}\n`))
await new Promise((r) => setTimeout(r, 200))
}
controller.close()
},
})
return new Response(stream, {
headers: { "Content-Type": "text/event-stream" },
})
})
npm モジュール
import { z } from "npm:zod@3"
import postgres from "npm:postgres"
import { Hono } from "jsr:@hono/hono"
const app = new Hono()
app.get("/", (c) => c.json({ ok: true }))
Deno.serve(app.fetch)
共有コード(_shared)
複数の関数で使う型やヘルパは _shared/ に置く:
supabase/functions/
├── _shared/
│ └── cors.ts
├── hello/
│ └── index.ts
└── webhook/
└── index.ts
制限
- 実行時間: 最大 150 秒(無料は 25 秒)
- メモリ: 256 MB
- リクエストサイズ: 10 MB(増やせる)
- ペイロード: stream 対応
- Cold start あり(Deno 製で速いが完全ゼロではない)
ロギング
console.log("info")
console.error("error", err)
// Dashboard → Edge Functions → Logs で見える
// supabase functions logs hello でも
OpenAI / Anthropic ラッパ例
フロントから直接 LLM プロバイダを叩くと API キーが漏れる。Edge Functions でバックエンドを薄く挟む:
// supabase/functions/chat/index.ts
import Anthropic from "npm:@anthropic-ai/sdk"
const client = new Anthropic({ apiKey: Deno.env.get("ANTHROPIC_API_KEY")! })
Deno.serve(async (req) => {
// ユーザ認証
const supabase = createClient(URL, ANON_KEY, {
global: { headers: { Authorization: req.headers.get("Authorization")! } },
})
const { data: { user } } = await supabase.auth.getUser()
if (!user) return new Response("Unauthorized", { status: 401 })
const { messages } = await req.json()
// ストリーミング
const stream = await client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages,
})
return new Response(stream.toReadableStream(), {
headers: { "Content-Type": "text/event-stream" },
})
})
失敗パターン
| 症状 | 原因 |
|---|---|
| 401 | verify-jwt が ON で Authorization ヘッダ欠如 |
| CORS エラー | OPTIONS 応答忘れ |
| 環境変数 undefined | secrets set 忘れ / デプロイ忘れ |
| Cold start で遅い | 軽量化、定期 ping、Pro へ |
| Edge で動かない依存 | npm ライブラリのfs / Buffer依存に注意 |
| 関数間で値が共有されない | 各実行は独立。共有は DB 経由 |
セルフホスト
オープンソース版はedge-runtime(Deno ベース)が同梱され、Docker 内で動く。
使うべき場面
API キーを隠す / 認証後の処理 / Webhook / 定期ジョブに最適。 単に DB を読み書きするだけなら supabase-js だけで完結するので不要。