タイトル長い。 LambdaでGoが正式サポートされるのを首を長くして待ちつつ、Apex で Go を実行しています。
先日、API Gatewayで受けたすべてのリクエストをLambdaに丸投げすることができるようになりました。
これまではAPI Gatewayでいちいちマッピング定義を作るのが面倒で、いまいち普通のWebAPI的なものをLambdaで作る気がしなかったわけですが、これで行けるのでは…? と思って、catch all されたリクエストとレスポンスを net/http.Request と net/http.ResponseWriter で扱えるようにする ridge というライブラリを書いてみました。
Readmeそのままですが、以下のような Go のコードが API Gateway (プロキシ統合)+ Lambda + Apex で動きます。
package main import ( "encoding/json" "fmt" "log" "net/http" "os" "github.com/apex/go-apex" "github.com/fujiwara/ridge" ) var mux = http.NewServeMux() func init() { mux.HandleFunc("/", handleRoot) mux.HandleFunc("/hello", handleHello) } func main() { if os.Getenv("APEX") == "" { // ローカルで動かしたい場合はこっち log.Println("starting up with local httpd") log.Fatal(http.ListenAndServe(":8080", mux)) } // Lambdaで動く場合はこっち apex.HandleFunc(func(event json.RawMessage, ctx *apex.Context) (interface{}, error) { r, err := ridge.NewRequest(event) if err != nil { log.Println(err) return nil, err } w := ridge.NewResponseWriter() mux.ServeHTTP(w, r) return w.Response(), nil }) } func handleHello(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Fprintf(w, "Hello %s\n", r.FormValue("name")) } func handleRoot(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, "Hello World") fmt.Fprintln(w, r.URL) }
- 入力の JSON を
*net/http.Request
に変換してアプリケーションに渡す ridge.ResponseWriter
はnet/http.ResponseWriter
interfaceを持っているのでそれに対してレスポンスを書き込む
とすると、普通に Lambda で Go の webapp らしきものが動きます。
ユーザの書くコードは普通の Go の webapp と同様 func(http.ResponseWriter, *http.Request)
で行けるので、ローカルでは http を直接 listen するサーバとして動かしつつ、API Gateway + Lambda でも同じコードを動かせる……これはもしかして凄く便利なのでは!
制限とか
- URL引数に同名の値 (
foo=1&foo=2
みたいなの) があると、入力Eventの時点でひとつになってしまう - HTTPリクエスト/レスポンスヘッダで複数同じものがあっても同様
- バイナリの入出力ができるのかどうか良く分からない
という現時点の制約があります。ちゃんとバイナリが扱えれば、画像変換サーバみたいなものにも使えて夢が広がる感じなのですが…