GAE/Go（2nd Gen）でDatastoreモードのFirestoreとFirestoreを使う

# GAE/Goでのデータストア
GAE/GoでのデータストアはCloudSQLか、DatastoreモードのFirestore、Firestore、その他外部サービスを利用する方法が考えられます。（以前は、GAEのAPIにDatastoreがあったのですが、今は、[DatastoreモードのFirestoreを使う](https://cloud.google.com/appengine/docs/standard/go/using-cloud-datastore?hl=ja)ことになっているようです。）この記事では、DatastoreモードのFirestoreを使う方法と、Firestoreを使う方法をまとめておきます。

## 本題に入る前の注意点
DatastoreモードのFirestoreを使うか、Firestoreを使うかは、GCPのプロジェクトでDatastoreを選択した時点で固定されてしまいますので注意してください。（興味本位でやっちゃうと、プロジェクトごと削除するしかなくなってしまう…）

## DatastoreモードのFirestoreを使う。
DatastoreモードのFirestoreを使う場合は、go getでパッケージをダウンロードします。（私は-uオプションを付けていてダウンロードに苦労したので、オプションなしで実行した方が良いかもしれません）

```bash:
$ go get cloud.google.com/go/datastore
```

プログラムは以前のDatastoreを使っていたのと同じようにプログラムできます。（個人的にはこちらの方が慣れているので便利に使えそうな予感がしています）

```go:main.go
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"cloud.google.com/go/datastore"
)

type Data struct {
	Name    string
	Checkin bool
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}

	ctx := context.Background()
	cli, err := datastore.NewClient(ctx, [プロジェクトID])
	if err != nil {
		log.Fatalf("Datastore NewClient Error %v", err)
	}
	defer cli.Close()

	k := datastore.IncompleteKey("Test", nil)
	d := Data{
		Name:    "Hoge",
		Checkin: true,
	}
	if _, err = cli.Put(ctx, k, &d); err != nil {
		log.Fatalf("Datastore Put Error %v", err)
	}

	fmt.Fprint(w, "Hello, World!")
}

func main() {
	http.HandleFunc("/", indexHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}

```

### ローカル環境での実行
Datastoreエミュレータを起動しておいてから、プログラムを実行します。

ローカルエミュレータのインストールコマンドは以下のようにgcloudコマンドで行います。

```bash:
$ gcloud components install cloud-datastore-emulator
```

ローカルエミュレータの実行もgcloudコマンドでできます。（WindowsでProgram Files以下にインストールしていると、起動に失敗して、表示されたコマンドを直接実行する必要があるかもしれません。私のWindows環境では失敗したので出力したものを再度実行しました。）

```bash:
$ gcloud beta emulators datastore start
```

アプリケーションを実行する前に環境変数を設定するようにメッセージがでるので指示に従います。

```bash:
$ export DATASTORE_EMULATOR_HOST=localhost:8081
$ go run main.go
```

### ローカル環境のデータストアを見る方法
ローカル環境のデータストアを見るためのコンソールがないので、[google-cloud-gui](https://github.com/GabiAxel/google-cloud-gui)をインストールしておくことをおすすめします。（UnitTestを実行するだけなら見ることはないかも？）


### 本番環境での実行
デプロイしたら以前と同様に実行できました。

## Firestoreを使う。
Firestoreを使う場合は、go getでパッケージをダウンロードします。（私は-uオプションを付けていてダウンロードに苦労したので、オプションなしで実行した方が良いかもしれません）

```bash:
$ go get cloud.google.com/go/firestore
```

プログラムは以下のようにします。（公開されているサンプルのままです）

```go:main.go
package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	"cloud.google.com/go/firestore"
)

func indexHandler(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}

	ctx := context.Background()
	client, err := firestore.NewClient(ctx, [プロジェクトID])
	if err != nil {
		log.Fatalf("Firestore NewClient Error: %v", err)
	}
	defer client.Close()

	_, _, err = client.Collection("users").Add(ctx, map[string]interface{}{
		"first": "Ada",
		"last":  "Lovelace",
		"born":  1815,
	})
	if err != nil {
		log.Fatalf("Failed adding alovelace: %v", err)
	}

	fmt.Fprint(w, "Hello, World!")
}

func main() {
	http.HandleFunc("/", indexHandler)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
		log.Printf("Defaulting to port %s", port)
	}

	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
}
```

### ローカル環境での実行
Firestoreエミュレータを起動しておいてから、プログラムを実行します。

ローカルエミュレータのインストールコマンドは以下のようにgcloudコマンドで行います。

```bash:
$ gcloud components install cloud-firestore-emulator
```

ローカルエミュレータの実行もgcloudコマンドでできます。（こちらは失敗しませんでした）

```bash:
$ gcloud beta emulators firestore start
```

```bash:
$ go run main.go
```

### 本番環境での実行
何も気にせずデプロイしたら実行できますが、なぜかコンソールでFirestoreのデータが見れず、firebaseのコンソールでは表示されていました。少し注意が必要かもしれません。
しかし、[ServiceAccount認証を設定](https://cloud.google.com/firestore/docs/quickstart-servers)してローカル環境で実行したときはGAEのコンソールでデータを見ることができたので、サービスアカウントを使えば見ることができるかもしれません。

## 結論
2020.03.01 修正
~~今の所、昔のDatastore（DatastoreモードのFirestore）も使えますが、そのうちFirestoreに移行させられそうなので、今のうちにFirestoreに慣れておいた方が良いかもしれません。~~

DatastoreモードのFirestoreとNativeモードのFirestoreは、機能とQuotaが異なるもののようなので、アプリケーションの仕様とデータストアの用途に合わせて使い分けるのが良いようです。（@sinmetalさん、ありがとうございます。そして、レビューが超高速ｗ）

# Disclaimer
* この記事は個人的なものです｡私の雇用者とは全く関係はありません｡（一応つけておきます）