LoginSignup
0
0

More than 3 years have passed since last update.

GAE/Go + EchoでFirestoreを導入してみた

Last updated at Posted at 2019-07-06

概要

まだGCP上で既存プロジェクトを扱う際にはDataStoreをそのまま使うということも多いけど、新しいプロジェクトを作成する際にはやっぱりFirestoreを使っていきたいよねー。

そんなわけで、構築&動作確認をした際のメモ書きです。

参考:
Go + Firestore (サーバー側) でローカルエミュレーターを使ってテストする
https://qiita.com/castaneai/items/c7d68cbee1a6e3655247

開発環境

環境は以下の通りです。

  • Mac OSX(10.14.5)
  • Golang 1.9
  • Echo 3.1.0
  • AppEngine 1.6.1

環境構築編

gcloud componentsのアップデート

あまり調べてないけど、ぼくが入れていたgcloudのバージョンでは対応してなかったので、コンポーネントの更新を行いました。

$ gcloud components update

Your current Cloud SDK version is: 206.0.0
You will be upgraded to version: 253.0.0

┌─────────────────────────────────────────────────────────────────────────────┐
│                      These components will be updated.                      │
├─────────────────────────────────────────────────────┬────────────┬──────────┤
│                         Name                        │  Version   │   Size   │
├─────────────────────────────────────────────────────┼────────────┼──────────┤
│ App Engine Go Extensions                            │     1.9.70 │ 56.4 MiB │
│ BigQuery Command Line Tool                          │     2.0.44 │  < 1 MiB │
│ BigQuery Command Line Tool (Platform Specific)      │     2.0.34 │  < 1 MiB │
│ Cloud SDK Core Libraries                            │ 2019.06.28 │ 11.0 MiB │
│ Cloud SDK Core Libraries (Platform Specific)        │ 2018.09.24 │  < 1 MiB │
│ Cloud Storage Command Line Tool                     │       4.39 │  3.6 MiB │
│ Cloud Storage Command Line Tool (Platform Specific) │       4.34 │  < 1 MiB │
│ gRPC python library                                 │     1.20.0 │  1.9 MiB │
│ gcloud Beta Commands                                │ 2019.05.17 │  < 1 MiB │
│ gcloud app Python Extensions                        │     1.9.86 │  6.0 MiB │
│ gcloud cli dependencies                             │ 2019.05.03 │  2.4 MiB │
│ gcloud cli dependencies                             │ 2018.08.03 │  1.5 MiB │
└─────────────────────────────────────────────────────┴────────────┴──────────┘
┌───────────────────────────────────────────────┐
│      These components will be installed.      │
├──────────────────────────┬─────────┬──────────┤
│           Name           │ Version │   Size   │
├──────────────────────────┼─────────┼──────────┤
│ Cloud Datastore Emulator │   2.1.0 │ 18.4 MiB │
└──────────────────────────┴─────────┴──────────┘

A lot has changed since your last upgrade.  For the latest full release notes,
please visit:
  https://cloud.google.com/sdk/release_notes

Do you want to continue (Y/n)?  Y
# あとは省略

goappを使えるようにする

gcloudを更新すると、Macだとだいたいgoappが使えなくなっちゃいます。
(本当は goapp をやめてgcloudで完結させたいんだけど、面倒だから今もgoappでローカル検証 + deployしてます)

$ goapp serve app-sandbox.yaml
-bash: /Users/xxx/tools/google-cloud-sdk/platform/google_appengine/goapp: Permission denied

こんな感じにエラーになっちゃうので、いつものように権限を通します。

$ sudo chmod +x ~/tools/google-cloud-sdk/platform/google_appengine/goapp
# 使えるようになったことを確認
$ goapp
Go is a tool for managing Go source code.

Usage:

    goapp command [arguments]

The commands are: (省略)

firestore emuratorを起動

はじめて使う場合、エミュレーターがダウンロードされます。
今後はローカルで検証する際には毎回 firestoreのエミュレーターを起動することになるみたい。

$  gcloud beta emulators firestore start
You need the [cloud-firestore-emulator] component to use the Google
Cloud Firestore emulator.


Your current Cloud SDK version is: 253.0.0
Installing components from version: 253.0.0

┌───────────────────────────────────────────────┐
│      These components will be installed.      │
├──────────────────────────┬─────────┬──────────┤
│           Name           │ Version │   Size   │
├──────────────────────────┼─────────┼──────────┤
│ Cloud Firestore Emulator │   1.6.0 │ 41.1 MiB │
└──────────────────────────┴─────────┴──────────┘

For the latest full release notes, please visit:
  https://cloud.google.com/sdk/release_notes

Do you want to continue (Y/n)?  Y

╔════════════════════════════════════════════════════════════╗
╠═ Creating update staging area                             ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Installing: Cloud Firestore Emulator                     ═╣
╠════════════════════════════════════════════════════════════╣
╠═ Creating backup and activating new installation          ═╣
╚════════════════════════════════════════════════════════════╝

Performing post processing steps...done.

Update done!

Restarting command:
  $ gcloud beta emulators firestore start

Executing: /Users/xxx/tools/google-cloud-sdk/platform/cloud-firestore-emulator/cloud_firestore_emulator start --host=::1 --port=8915
[firestore] API endpoint: http://::1:8915
[firestore] If you are using a library that supports the FIRESTORE_EMULATOR_HOST environment variable, run:
[firestore]
[firestore]    export FIRESTORE_EMULATOR_HOST=::1:8915
[firestore]
[firestore] Dev App Server is now running.

実際に動かしてみる

yamlファイルの修正

FIRESTORE_EMULATOR_HOST の定数を入れろということなので、 yamlファイルに以下のように記載。

app.yaml
application: testProject
module: default
version: 1
runtime: go
api_version: go1.9
threadsafe: true

handlers:
    - url: /.*
      script: _go_app

skip_files:
    - (.*/)?vendor/(.*/)?
    - (.*/)?golang\.org/(.*/)?
    - (.*/)?pkg/(.*/)?

automatic_scaling:
    max_concurrent_requests: 40
    min_idle_instances: 0
    max_idle_instances: 1
    max_pending_latency: automatic
    min_pending_latency: 3000ms

env_variables:
  FIRESTORE_EMULATOR_HOST: "localhost:8915" # 追加

動作確認

とりあえずは動けばいいレベルで。
GET /debug を叩くと登録され、 GET /debug/{id} を叩くとそのIDのものが取得できるだけのやつ。

main.go

import (
    "context"
    "github.com/labstack/echo"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine"
    "net/http"
    "project/app/core"
    "project/app/handler"
    "github.com/gorilla/sessions"
    "github.com/labstack/echo-contrib/session"
    "project/app/middlewares"
    "firebase.google.com/go"
    "fmt"
)


const AppEngine = "appengine"
var e = createMux()

func init() {
    {
        ah := e.Group("/_ah")
        ah.GET("/start", func(c echo.Context) error { return c.String(http.StatusOK, "") })
    }


    g := e.Group("/debug")
    g.GET("", func(c echo.Context) error {
        ctx, ok := c.Get(AppEngine).(context.Context)
        if !ok {
            return c.String(http.StatusInternalServerError, "error")
        }
        app, err := firebase.NewApp(ctx, nil)
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }
        store, err := app.Firestore(ctx)
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }
        doc, _, err := store.Collection("users").Add(ctx, map[string]interface{}{
            "name": "hello",
        });
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }

        return c.String(http.StatusOK, fmt.Sprintf("DocID: %s", doc.ID))
    });

    g.GET("/:id", func(c echo.Context) error {
        ctx, ok := c.Get(AppEngine).(context.Context)
        if !ok {
            return c.String(http.StatusInternalServerError, "error")
        }
        app, err := firebase.NewApp(ctx, nil)
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }
        store, err := app.Firestore(ctx)
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }
        dsnap, err := store.Collection("users").Doc(c.Param("id")).Get(ctx)
        if err != nil {
            return c.String(http.StatusInternalServerError, fmt.Sprintf("failed (reason: %+v)", err))
        }
        return c.String(http.StatusOK, fmt.Sprintf("DocID: %+v", dsnap.Data()));
    })

}

func UseAppEngine() echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            ctx := appengine.NewContext(c.Request())
            c.Set(AppEngine, ctx)
            return next(c)
        }
    }
}

func createMux() *echo.Echo {
    e := echo.New()
    e.Use(core.UseAppEngine())
    http.Handle("/", e)
    return e
}

GET /debug を叩く

image.png

GET /debug/:id を叩く

image.png

なるほどねー。

所感

導入自体は特に何もつまるところはなかったです。 firestoreのAPIの学習はこれからなのでよくわかってないです(´・_・`)
一応 DataStoreと互換性あるって話だけど、 goon は使えないんだろうなぁ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0