API Gateway + Lambdaでcatch allした処理をApex + Goでnet/httpで扱える ridge を書いた

タイトル長い。 LambdaでGoが正式サポートされるのを首を長くして待ちつつ、Apex で Go を実行しています。

先日、API Gatewayで受けたすべてのリクエストをLambdaに丸投げすることができるようになりました。

これまではAPI Gatewayでいちいちマッピング定義を作るのが面倒で、いまいち普通のWebAPI的なものをLambdaで作る気がしなかったわけですが、これで行けるのでは…? と思って、catch all されたリクエストとレスポンスを net/http.Request と net/http.ResponseWriter で扱えるようにする ridge というライブラリを書いてみました。

github.com

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.ResponseWriternet/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リクエスト/レスポンスヘッダで複数同じものがあっても同様
  • バイナリの入出力ができるのかどうか良く分からない

という現時点の制約があります。ちゃんとバイナリが扱えれば、画像変換サーバみたいなものにも使えて夢が広がる感じなのですが…

ともあれ現時点でも、普通にHTMLやJSONを返すAPIを実装するのには使えるはずですので、是非お試しください。