🐁

Supabase Edge Functions への Go コード無料デプロイに挑む

2024/11/04に公開

はじめに

Supabase をご存知でしょうか?

https://supabase.com/

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 というサービスはなんでもできそうな魅力的なサービスです。

https://supabase.com/docs/guides/functions

この Edge Functions に、ついに、ようやく、Go コードをデプロイすることに成功しました。
Go コードを呼び出してログ出力する程度の道半ばではありますが一旦整理するために軽く記事にしてご紹介します。

TL;DR

ちなみに私は DenoWebAssembly もそこまで詳しくないので悪しからず。。。

Supabase とは

https://supabase.com/

前述した通りオープンソースの Firebase 代替として利用できるフルスタックなバックエンドサービスです。
Database, Auth, Edge Functions, Storage, Realtime, Vector とさまざまなサービスを利用することが可能です。

Supabase をローカルで使う

Supabase はローカル環境に簡単に構築することができるので開発がしやすいです。
詳しく解説されている方がいらっしゃったのでここでの説明は省略します。

https://zenn.dev/slowhand/articles/209699774226af

準備 & version

以下の公式ドキュメントに従い Supabase CLI を使えるようにします。

https://supabase.com/docs/guides/local-development/cli/getting-started

バージョン情報は以下となります。

supabase --version
1.207.9
go version
go version go1.23.2 darwin/arm64

デプロイまでの試行錯誤

Deno も WebAssembly もそこまで詳しくないのでそもそもどうやって動かすんだろうからスタートです。

とりあえず記事を探して2つほど方法を見つけましたがいずれも失敗しました。。。

ファイル読み込み

https://dev.to/taterbase/running-a-go-program-in-deno-via-wasm-2l08

https://ricomintea.hatenablog.jp/entry/2023/08/05/190750

runtime has escaped from the event loop unexpectedly: event loop error: PermissionDenied: Deno.open is blocklisted

fetch 読み込み

https://zenn.dev/askua/articles/9b614c377cc1e0

failed to send request to user worker: event loop error: TypeError: NetworkError when attempting to fetch resource.

完成系

どちらの方法も権限まわりで実行時に失敗します。
結局どうやって解決したか。
syumai さんのコードを読みました。
前から以下のポストの存在を知っていたのですがいまいちなにをしているのかピンと来ていませんでした。

https://x.com/__syumai/status/1380752314969976834

https://github.com/syumai/deno-deploy-scale-image

が、自分の手を動かしてようやく syumai さんが何をしていたのかわかりました。
なるほど、面白すぎる...

https://github.com/syumai/deno-deploy-scale-image/blob/main/mod.js

冒頭でも記載した通り GOOS=js GOARCH=wasm go build -o main.wasm main.go でビルドした結果を base64 でエンコードしてインポートが行われています。

この記事を書くためにこのポストを探していたのですが base64 でエンコードするツールも実装されていますね。

https://github.com/syumai/binpack

あとで組み込もうと思います。

デプロイ & 動作確認

最終的にデプロイしたコードは以下です。

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として保存します。

https://www.npmjs.com/package/@types/golang-wasm-exec

それをindex.ts で下記のように実装し参照します。

+ /// <reference path="./wasm_exec.d.ts" />

おわりに

今回はとりあえず Go のコードを Edge Functions で動かした程度でした。
最終的には syumai さんが作成されている syumai/workers のようなことがしたいのでもう少し遊ぼうと思います!

https://github.com/syumai/workers

デプロイログを見ると

Deploying Function: health (script size: 1.013MB)

とあります。
ファイルが大きすぎる。。。大抵無料でデプロイできるサービスは制限があるのでおそらく詰みます。
Edge Functions がどれくらいの大きさをデプロイできるかわからなかったのですがサイズを小さくするために tinygo などを使う必要がありそうです。

Discussion