go言語でCloud Functions + FireStoreをhttpで使ってみた

goのCloud FunctionsがBetaじゃなくなったので、使ってみた 一般的にはhttpよりもGCSとかPUB/SUB周りで使用するイメージですが、今回はhttpでサンプルを作っていきます。(httpならGAEでも良かったけど。。。)

まずはチュートリアル

※gcloudの設定周りはできている前提

以下のファイル作成

■ hello_world.go

package helloworld

import (
    "fmt"
    "net/http"
)

// HelloGet is an HTTP Cloud Function.
func HelloGet(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, World!")
}

で、コマンド実行

$gcloud functions deploy HelloGet --runtime go111 --trigger-http

で確認

$curl  https://us-central1-プロジェクトID.cloudfunctions.net/HelloGet
Hello, World!

デプロイできました

FireStoreを使ってみる

firebase.google.com

Firebaseを導入

$go get firebase.google.com/go

Firebaseのクライアントを取得

  • Firebaseのクライアントはサービスアカウントの鍵を生成して認証

■firestore.go

package app

import (
    "context"

    "cloud.google.com/go/firestore"
    firebase "firebase.google.com/go"
    "google.golang.org/api/option"
)

func getFireBaseClient(ctx context.Context) (*firestore.Client, error) {
    opt := option.WithCredentialsFile("serviceAccountKey.json")
    app, err := firebase.NewApp(ctx, nil, opt)
    if err != nil {
        return nil, err
    }

    return app.Firestore(ctx)
}

データを保存

Collectionを「results」に設定 ドキュメントにUUIDを設定して、そこに「item」を設定する

package app

import (
    "context"
    "encoding/json"
    "net/http"
    "time"

    "github.com/rs/xid"
)

func SaveItem(w http.ResponseWriter, r *http.Request) {
    ctx := context.Background()

    client, err := getFireBaseClient(ctx)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    jst, err := time.LoadLocation("Asia/Tokyo")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    guid := xid.New()
    item := Item{
        Status: "OK",
        Day:    time.Now().In(jst).Format("2006-01-02"),
    }
    _, err = client.Collection("results").Doc(guid.String()).Set(ctx, item)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    res, err := json.Marshal(item)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Write(res)
}

データを取得

Collectionを「results」に設定してWhereで「day」が今日の日付になっているデータを取得

package app

import (
    "context"
    "encoding/json"
    "net/http"
    "time"
)

func GetItem(w http.ResponseWriter, r *http.Request) {
    ctx := context.Background()

    client, err := getFireBaseClient(ctx)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    jst, err := time.LoadLocation("Asia/Tokyo")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    day := time.Now().In(jst).Format("2006-01-02")

    matchItem := client.Collection("results").Where("day", "==", day).Documents(ctx)
    docs, err := matchItem.GetAll()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    data := []interface{}{}
    for _, doc := range docs {
        data = append(data, doc.Data())
    }

    w.Header().Set("Content-Type", "application/json")
    res, err := json.Marshal(data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Write(res)
}

デプロイ

$gcloud functions deploy SaveItem --runtime go111 --trigger-http
$gcloud functions deploy GetItem --runtime go111 --trigger-http

これで完成

github

その内、自分でも使いそうなので忘れないようにリポジトリを作っておきました。

github.com

最後

GAEで良いような気もするけど、たまにバージョンを作りすぎてデプロイできなくなるから、実験用のAPIはCloud Functionsでも良いかも