Supabase Edge Functions への Go コード無料デプロイに挑む
はじめに
Supabase をご存知でしょうか?
Supabase is an open source Firebase alternative.
Start your project with a Postgres database, Authentication, instant APIs, Edge Functions, Realtime subscriptions, Storage, and Vector embeddings.
Firebase に替わるオープンソースと記載があります。
魅力はたくさんありますがなんといっても素晴らしいのは無料で使えるということです。
中でも Edge Functions というサービスはなんでもできそうな魅力的なサービスです。
この Edge Functions に、ついに、ようやく、Go コードをデプロイすることに成功しました。
Go コードを呼び出してログ出力する程度の道半ばではありますが一旦整理するために軽く記事にしてご紹介します。
TL;DR
ちなみに私は Deno も WebAssembly もそこまで詳しくないので悪しからず。。。
Supabase とは
前述した通りオープンソースの Firebase 代替として利用できるフルスタックなバックエンドサービスです。
Database, Auth, Edge Functions, Storage, Realtime, Vector とさまざまなサービスを利用することが可能です。
Supabase をローカルで使う
Supabase はローカル環境に簡単に構築することができるので開発がしやすいです。
詳しく解説されている方がいらっしゃったのでここでの説明は省略します。
準備 & version
以下の公式ドキュメントに従い Supabase CLI を使えるようにします。
バージョン情報は以下となります。
supabase --version
1.207.9
go version
go version go1.23.2 darwin/arm64
デプロイまでの試行錯誤
Deno も WebAssembly もそこまで詳しくないのでそもそもどうやって動かすんだろうからスタートです。
とりあえず記事を探して2つほど方法を見つけましたがいずれも失敗しました。。。
ファイル読み込み
runtime has escaped from the event loop unexpectedly: event loop error: PermissionDenied: Deno.open is blocklisted
fetch 読み込み
failed to send request to user worker: event loop error: TypeError: NetworkError when attempting to fetch resource.
完成系
どちらの方法も権限まわりで実行時に失敗します。
結局どうやって解決したか。
syumai さんのコードを読みました。
前から以下のポストの存在を知っていたのですがいまいちなにをしているのかピンと来ていませんでした。
が、自分の手を動かしてようやく syumai さんが何をしていたのかわかりました。
なるほど、面白すぎる...
冒頭でも記載した通り GOOS=js GOARCH=wasm go build -o main.wasm main.go
でビルドした結果を base64
でエンコードしてインポートが行われています。
この記事を書くためにこのポストを探していたのですが base64 でエンコードするツールも実装されていますね。
あとで組み込もうと思います。
デプロイ & 動作確認
最終的にデプロイしたコードは以下です。
index.ts
import "./wasm_exec.js";
import mainwasm from "./mainwasm.ts";
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { decode } from "https://deno.land/std@0.139.0/encoding/base64.ts";
const go = new Go();
const module = decode(mainwasm);
Deno.serve(async (req) => {
const { name } = await req.json();
const data = {
message: `Hello ${name}!`,
};
const result = await WebAssembly.instantiate(module, go.importObject);
go.run(result.instance);
return new Response(
JSON.stringify(data),
{ headers: { "Content-Type": "application/json" } },
);
});
※ ほぼ supabase functions new xxx
で出力されたコードです。
main.go
package main
import (
"log/slog"
)
func main() {
slog.Info("WASM Go Initialized")
}
mainwasm.ts
export default "xxxxxxxxx"
※ main.wasm を base64 でエンコードしたもの
いずれのファイルも同じディレクトリに配置しています。
ローカル環境
まずはローカル環境で動作確認を試します。
下記のコマンドにて起動します。
supabase start
supabase functions serve
そして cURL にて打鍵を行います。
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/health' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
--header 'Content-Type: application/json' \
--data '{"name":"Function"}'
以下のようにレスポンスが出力されます。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 25
Connection: keep-alive
vary: Accept-Encoding
date: Mon, 04 Nov 2024 08:46:15 GMT
X-Kong-Upstream-Latency: 252
X-Kong-Proxy-Latency: 3
Via: kong/2.8.1
{"message":"Hello Function!"}
また、Supabase を起動しているシェルにも以下のログが出力されます。
[Info] 2024/11/04 08:46:15 INFO WASM Go Initialized
無事、Go のコードが実行されていることが確認できました!
クラウド環境
続いてクラウド環境にデプロイします。
以下のコマンドにてデプロイを実施します。
※ jwt を検証しないために --no-verify-jwt
オプションを有効にしました。
supabase functions deploy health --project-ref xxxxxxxxxx --no-verify-jwt
以下のようなログが出力され、成功したことが確認できます。
No seed files matched pattern: supabase/seed.sql
Bundling Function: health
Download https://deno.land/std@0.139.0/encoding/base64.ts
Download https://jsr.io/@supabase/functions-js/meta.json
Download https://jsr.io/@supabase/functions-js/2.4.3_meta.json
Deploying Function: health (script size: 1.013MB)
Deployed Functions on project xxxxxxxxxx: health
You can inspect your deployment in the Dashboard: https://supabase.com/dashboard/project/xxxxxxxxxx/functions
同じく cURL にて打鍵を行います。
curl -i --location --request POST 'https://xxxxxxxxxx.supabase.co/functions/v1/health' \
--header 'Content-Type: application/json' \
--data '{"name":"Functions"}'
以下のようにレスポンスが出力されます。
HTTP/2 200
date: Mon, 04 Nov 2024 09:13:41 GMT
content-type: application/json
cf-ray: 8dd37d3eadf6e380-NRT
cf-cache-status: DYNAMIC
strict-transport-security: max-age=31536000; includeSubDomains
vary: Accept-Encoding
x-deno-execution-id: df6eb09e-db85-4a5a-86ed-c25c2b543807
x-sb-edge-region: ap-northeast-1
x-served-by: supabase-edge-runtime
server: cloudflare
alt-svc: h3=":443"; ma=86400
{"message":"Hello Functions!"}
また、Supabase Edge Functions のダッシュボードにてログが出力されていることを確認できます。
2024/11/04 09:13:41 INFO WASM Go Initialized
ほかにもハマった
開発環境として VSCode を選択しているのですが、
index.ts で import "./wasm_exec.js";
をしても以下のメッセージが VSCode で出力されていました。
Cannot find name 'Go'. Did you mean 'go'?deno-ts(2552)
index.ts: 'go' is declared here.
これを解消しなくてもビルド&デプロイは実行でき動作も問題なく動くのですが気持ちよくコーディングできなかったので以下のような対応をしています。
wasm_exec.js
の型情報を入手し、wasm_exec.d.ts
として保存します。
それをindex.ts
で下記のように実装し参照します。
+ /// <reference path="./wasm_exec.d.ts" />
おわりに
今回はとりあえず Go のコードを Edge Functions で動かした程度でした。
最終的には syumai さんが作成されている syumai/workers のようなことがしたいのでもう少し遊ぼうと思います!
デプロイログを見ると
Deploying Function: health (script size: 1.013MB)
とあります。
ファイルが大きすぎる。。。大抵無料でデプロイできるサービスは制限があるのでおそらく詰みます。
Edge Functions がどれくらいの大きさをデプロイできるかわからなかったのですがサイズを小さくするために tinygo などを使う必要がありそうです。
Discussion