GAE(Go1.12)でDatastoreに接続してみるぞ
概要
GAE(Go1.12)でDatastoreに接続してみる
構築
Datastoreへ接続
Datastoreに接続するための初期化処理開始
init
メソッドで初期化処理を実施
Datastoreを操作するDao的なDBを作成していく!!
internal/config.go
var ( DB repository.MemberDatabase ) func init(){ var err error DB, err = configureDatastore("ucwork-ai-000002") if err != nil { log.Fatal(err) } }
Datastoreに接続するためのclient作成
projectIDを指定してDatastoreに接続するためのclientを作成
internal/config.go
func configureDatastore(projectID string) (repository.MemberDatabase, error){ ctx := context.Background() client, err := datastore.NewClient(ctx, projectID) if err != nil { return nil, err } return db.NewDatastoreDB(client) }
Datastoreへの接続確認と構造体の完成
NoSQLってトランザクションないんちゃったっけ。
トランザクション使ってDatastoreへ接続できるか確認
接続できることを確認したら対象のclientをdatastoreDBへ設定し返却
戻り値の型がrepository.MemberDatabase
なのでこの構造体はDB操作系のメソッドを持つのである。
internal/db/datastore.go
type datastoreDB struct { client *datastore.Client } var _ repository.MemberDatabase = &datastoreDB{} func NewDatastoreDB(client *datastore.Client) (repository.MemberDatabase, error) { ctx := context.Background() // Verify that we can communicate and authenticate with the datastore service. t, err := client.NewTransaction(ctx) if err != nil { return nil, fmt.Errorf("datastoredb: could not connect: %v", err) } if err := t.Rollback(); err != nil { return nil, fmt.Errorf("datastoredb: could not connect: %v", err) } return &datastoreDB{ client: client, }, nil }
Datastoreでの操作作成
インターフェース定義
とりあえず登録と一覧表示のメソッドを宣言
プロパティはなんでもいいからIDとNameを定義
internal/repository/member.go
// Member hold metadata about a Membe<ffff>r type Member struct { ID int64 Name string } // MemberDatabase provides access to a database od member type MemberDatabase interface { // ListMembers returns member list ListMembers() ([]*Member, error) // AddMember saves a given member, assigning it a new ID AddMember(member *Member) (id int64, err error) }
実装
client.Put
とかclient.GetAll
すると登録したり一覧取得できるらしい
internal/db/datastore.go
func (db *datastoreDB) AddMember(member *repository.Member) (id int64, err error){ ctx := context.Background() k := datastore.IncompleteKey("Member", nil) k, err = db.client.Put(ctx, k, member) if err != nil { return 0, fmt.Errorf("datastoredb: could not put Member: %v", err) } return k.ID, nil } func (db *datastoreDB) ListMembers() ([]*repository.Member, error) { ctx := context.Background() members := make([]*repository.Member, 0) q := datastore.NewQuery("Member"). Order("Name") keys, err := db.client.GetAll(ctx, q, &members) if err != nil { return nil, fmt.Errorf("datastoredb: could not list books: %v", err) } for i, k := range keys { members[i].ID = k.ID } return members, nil }
DB操作の呼び出し
Goでルーティング(gorilla/mux) で作成したルーティングに今回の処理を実装してみる。
jsonで受け取ってjsonで返却するAPIのイメージ。とりあえず動けばいいや感。
func createHandler(w http.ResponseWriter, r *http.Request) *appError { // json decode decoder := json.NewDecoder(r.Body) var memberRequest endpoint.MemberRequest err := decoder.Decode(&memberRequest) if err != nil { return appErrorFormat(err, "decode error: %s", err) } // object convert member, err := memberFromJson(&memberRequest) if err != nil { return appErrorFormat(err, "convert error: %s", err) } // save member to db id, err := internal.DB.AddMember(member) if err != nil { return appErrorFormat(err, "add db error: %s", err) } // create response response, jsonError := json.Marshal(member) if jsonError != nil { return appErrorFormat(jsonError, "%s", jsonError) } _, writeError := w.Write(response) if writeError != nil { return appErrorFormat(writeError, "%s", writeError) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Location", "/members/"+string(id)) w.WriteHeader(201) return nil } func listHandler(w http.ResponseWriter, r *http.Request) *appError { members, err := internal.DB.ListMembers() if err != nil { return appErrorFormat(err, "%s", err) } response, jsonError := json.Marshal(members) if jsonError != nil { return appErrorFormat(jsonError, "%s", jsonError) } _, writeError := w.Write(response) if writeError != nil { return appErrorFormat(writeError, "%s", writeError) } w.Header().Set("Content-Type", "application/json") return nil }
検証
ローカル環境起動
以下コマンド実行
go run cmd/ucwork/main.go
datastoreに登録
POST通信で登録してみると何やら返却された。
きっとうまくいってそう。本当は201だろとかあるけどdatastoreには登録できたっぽい
curl -v -X POST -H "Content-Type: application/json" -d '{"Name":"ucwork"}' http://localhost:8080/members +[add_datastore_connection#6] Note: Unnecessary use of -X or --request, POST is already inferred. * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) > POST /members HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.54.0 > Accept: */* > Content-Type: application/json > Content-Length: 17 > * upload completely sent off: 17 out of 17 bytes < HTTP/1.1 200 OK < Date: Wed, 25 Sep 2019 08:04:14 GMT < Content-Length: 24 < Content-Type: text/plain; charset=utf-8 < * Connection #0 to host localhost left intact {"ID":0,"Name":"ucwork"}%
datastoreから一覧取得
GETで一覧取得してみるとさっきの取れた!
curl -v http://localhost:8080/members +[add_datastore_connection#6] * Trying ::1... * TCP_NODELAY set * Connected to localhost (::1) port 8080 (#0) > GET /members HTTP/1.1 > Host: localhost:8080 > User-Agent: curl/7.54.0 > Accept: */* > < HTTP/1.1 200 OK < Date: Wed, 25 Sep 2019 08:05:56 GMT < Content-Length: 41 < Content-Type: text/plain; charset=utf-8 < * Connection #0 to host localhost left intact [{"ID":5631986051842048,"Name":"ucwork"}]%
コンソール上
コンソール上でも登録されてる!!
というか、ローカルから叩いても本物に登録されるのか...
gcloud beta emulators datastore start
使うとローカルで仮想的なdatastoreできるらしい。
とりあえず一通り触ってみるが目的なんで、今度ちゃんと触るときにローカルエミュレートしよー
まとめ
GCPというよりgoの学習に時間割かれてる気がする・・・
とりあえずDatastore触ったから次はCloudSQLかな。