EdgeFunction関連
ローカル開発では再起動と関数実行をひたすら繰り返すので、コマンドライン主体で修正サイクルを回すと早いです。
起動
起動
supabase functions serve
Background起動
supabase functions serve &
ログを書き出しながらバックグラウンド起動
supabase functions serve > functions.log 2>&1 &
バックグラウンドで起動したときの終了方法(fg + Ctrl-C以外)
pkill -f "supabase functions serve"
関数名を指定して起動。ただ今のところ関数をすべて起動してもそれほど重くなさそうなので一括で起動することが多いです。
supabase function serve somefunction
jwt認証を無効化して起動
supabase functions serve --no-verify-jwt
関数ごとにjwt認証無効など
config.toml
に関数ごとの設定の既定値を書いておけます。
[functions.somefunction]
verify_jwt = false
こうすると supabase function serve
したり supabase function deploy
したときに関数ごとに設定を個別に行うことができます。
ポート番号変更
これも設定。複数プロジェクトをローカルで同時に動かすときには10ずつずらしています。
[api]
enabled = true
# Port to use for the API URL.
port = 54411
アクセストークンを取得(jq利用)
ローカルでは 「--no-verify-jwt すれば開発が捗る」とはいえ、RLSが有効になっているテーブルに対してEdgeFunctionを動作させる場合にはユーザーの認証情報を引き継いで検証しながら開発をする必要があります。
まずは存在するユーザーでJWTトークンを取得。
ポート番号を変えたりしているので supabase status で確認しながら取得。
認証するユーザーとパスワードは変数で定義。
USER=user1@kater.jp PASSWORD=password123
curl -s -X POST $(supabase status --output json | jq -r '.API_URL')'/auth/v1/token?grant_type=password' \
-H "apikey: $(supabase status --output json | jq -r '.ANON_KEY')" \
-H "Content-Type: application/json" \
-d '{"email":"'${USER}'","password":"'${PASSWORD}'"}' | jq -r '.access_token'
JWTを持ってEgdeFunctionを叩く
curl -H "Authorization: Bearer $ACCESS_TOKEN" $(supabase status --output json | jq -r '.API_URL')/functions/v1/somefunction
まとめて実行
USER=user1@kater.jp PASSWORD=password123
ACCCESS_TOKEN=$(curl -s -X POST $(supabase status --output json | jq -r '.API_URL')'/auth/v1/token?grant_type=password' \
-H "apikey: $(supabase status --output json | jq -r '.ANON_KEY')" \
-H "Content-Type: application/json" \
-d '{"email":"'${USER}'","password":"'${PASSWORD}'"}' | jq -r '.access_token')
curl -H "Authorization: Bearer $ACCESS_TOKEN" $(supabase status --output json | jq -r '.API_URL')/functions/v1/somefunction
関数の再起動とまとめて実行
# 再起動
pkill -f "supabase functions serve" ; supabase functions serve > supabase/functions.log 2>&1 & sleep 2
# JWTの取得
USER=user1@kater.jp PASSWORD=password123
ACCCESS_TOKEN=$(curl -s -X POST $(supabase status --output json | jq -r '.API_URL')'/auth/v1/token?grant_type=password' \
-H "apikey: $(supabase status --output json | jq -r '.ANON_KEY')" \
-H "Content-Type: application/json" \
-d '{"email":"'${USER}'","password":"'${PASSWORD}'"}' | jq -r '.access_token')
# 関数を叩く
curl -H "Authorization: Bearer $ACCESS_TOKEN" $(supabase status --output json | jq -r '.API_URL')/functions/v1/somefunction
EdgeFunction内でユーザーをトークンのユーザに切り替え
const supabaseClient = createClient(
supabaseUrl ?? '', supabaseAnonKey ?? '',
{
global: {
headers: { Authorization: 'Bearer ' + req.headers.get('Authorization') || '' },
},
}
)
// ユーザーをANONからJWTのユーザーにすげ替える
const token = authHeader.replace('Bearer ', '')
const { data: { user }, error: authError } = await supabaseClient.auth.getUser(token)
下記の公式の記事を見ていたんですがうまくいかず。
で、公式サンプル とかを見ると createClient
のときの第三引数に global.headers
を与える必要があるっぽくて、それを与えるとうまくいきました。
下記です。
createClient(
supabaseUrl ?? '', supabaseAnonKey ?? '',
{
global: {
headers: { Authorization: 'Bearer ' + req.headers.get('Authorization') || '' },
},
}
)
DenoじゃなくてNodeで検証
EdgeFunctionの起動すら面倒なときはまずNodeで検証して移植すると良いです。でもDeno特有の問題とかあるので注意が必要。
Nodeで検証するサンプルコードと検証コマンド
import { createClient } from '@supabase/supabase-js';
const SUPABASE_URL = 'http://127.0.0.1:54411';
const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';
async function main(jwt: string) {
console.log('Received JWT:', jwt);
// Supabaseクライアントの作成
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY,
{
global: {
headers: { Authorization: 'Bearer ' + jwt },
},
}
);
try {
// ユーザー認証
// const { data: user, error: authError } = await supabase.auth.getUser(jwt)
const { data: user, error: authError } = await supabase.auth.getUser(jwt);
console.log('認証成功:', user?.user?.email);
// 画像の取得
const { data: imageData, error: imageError } = await supabase
.from('images')
.select('*')
.eq('id', '0287da2f-4058-891c-787e-7d68a7346ee8')
.single();
if (imageError) {
throw imageError;
}
if (!imageData) {
console.log('画像が見つかりませんでした');
return;
}
console.log('取得した画像データ:', imageData);
} catch (error) {
console.error('エラーが発生しました:', error);
}
}
// JWTを引数として渡して実行
if (process.argv.length < 3) {
console.error('使用方法: ts-node get_image2.ts <JWT>');
process.exit(1);
}
main(process.argv[2]);
USER=user1@kater.jp PASSWORD=password123
ACCCESS_TOKEN=$(curl -s -X POST $(supabase status --output json | jq -r '.API_URL')'/auth/v1/token?grant_type=password' \
-H "apikey: $(supabase status --output json | jq -r '.ANON_KEY')" \
-H "Content-Type: application/json" \
-d '{"email":"'${USER}'","password":"'${PASSWORD}'"}' | jq -r '.access_token')
# 関数実行
ts-node sample.ts $ACCESS_TOKEN
参考
DB関連
接続
psqlで接続。ポート番号とか変えてる事があるので supabase status を利用。
psql "$(supabase status --output json | jq -r '.DB_URL')
db reset マイグレーションからの再適応
supabase db reset
シーディングのみやり直し
db reset
だと時間がかかるので開発用のシードの調整を行う場合には truncate -> シード読み込み で時間短縮。
DO $$
DECLARE
r RECORD;
BEGIN
FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP
EXECUTE 'TRUNCATE TABLE public.' || quote_ident(r.tablename) || ' CASCADE';
END LOOP;
END $$;
複数のプロジェクトで使うのでGistに上げておきました。
このスクリプトを使ってDBのクリア。
curl -s https://gist.githubusercontent.com/yousan/a29149ffe7ba7459011a61cb9710674b/raw/06d03f4fd4a9593a719a4acffff92545ee6e0fab/truncate_all_data_at_public_schema.sql | psql "$(supabase status --output json | jq -r '.DB_URL')"
psql "$(supabase status --output json | jq -r '.DB_URL')" < supabase/seed.sql ;