Go
AWS
golang
GoogleAppEngine
GoogleCloudPlatform

🙂< golang+gae+datastore+echo+jsonのapiです - 其の弐

はじめに

appengine公式サンプル?を参考にしています。jsonでreq/resのやりとりすることを前提としています。

コード

前半と後半で分けます。前半はサーバ起動、ルーティング。後半はハンドラー関数とdatastore関連になります。ファイルはmain.goのみです。

前半

main.go
package main

import (
    "fmt"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "google.golang.org/appengine"
    "google.golang.org/appengine/datastore"
    "google.golang.org/appengine/log"
    "net/http"
    "time"
)

var e = createMux()

type Post struct {
    Author  string    `json:"author"`
    Message string    `json:"message"`
    Posted  time.Time `json:"posted"`
}

type templateParams struct {
    Notice string `json:"notice"`
    Posts  []Post `json:"posts"`
}

func main() {
    e := echo.New()
    e.Start(":8080")
    appengine.Main()
}

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

func init() {
    g := e.Group("/posts")
    g.Use(middleware.CORS())
    g.POST("", createPost)
    g.GET("", getPosts)
    g.GET("/:author", getPost)
}

後半

main.go
func createPost(c echo.Context) error {
    ctx := appengine.NewContext(c.Request())
    post := Post{}

    if err := c.Bind(&post); err != nil {
        return err
    }

    post.Posted = time.Now()
    key := datastore.NewIncompleteKey(ctx, "Post", nil)

    if _, err := datastore.Put(ctx, key, &post); err != nil {
        log.Errorf(ctx, "Put: %v", err)
        c.Response().WriteHeader(http.StatusInternalServerError)
        params := templateParams{}
        params.Notice = err.Error()
        return c.JSON(http.StatusInternalServerError, params)
    }

    params := templateParams{}
    params.Notice = "Success!!"
    return c.JSON(http.StatusOK, params)
}

func getPosts(c echo.Context) error {
    ctx := appengine.NewContext(c.Request())
    q := datastore.NewQuery("Post").Order("-Posted").Limit(20)
    params := templateParams{}

    if _, err := q.GetAll(ctx, &params.Posts); err != nil {
        log.Errorf(ctx, "Getting Post Error: %v", err)
        c.Response().WriteHeader(http.StatusInternalServerError)
        params.Notice = "Getting Post Error"
        return c.JSON(http.StatusInternalServerError, params)
    }
    params.Notice = fmt.Sprintf("Success")
    return c.JSON(http.StatusOK, params.Posts)
}

func getPost(c echo.Context) error {
    ctx := appengine.NewContext(c.Request())
    authorParam := c.Param("author")
    post := &Post{
        Author: authorParam,
    }
    q := datastore.NewQuery("Post").Filter("Author =", post.Author)  // .Filter("Country =", post.***) 条件増やすことも可能。
    it := q.Run(ctx)
    _, err := it.Next(post)
    if err != nil {
        log.Errorf(ctx, "New page: %v", err)
    }
    return c.JSON(http.StatusOK, post)
}

デプロイ及び使い方

terminal
$ gcloud app deploy

POSTMAN等でリクエストやレスポンスを確認してみてください。Datastoreに、Kindが作られ、エンティティが格納されているはずです。

余談

ある条件のデータを複数取り出したい場合のコードを備忘録として掲載しておきます。authorはechoフレームワークを用いて、URLパラメータから取得しています。これは余談です。

main.go

// パラメータを使い、クエリを出して、複数の返り値を出したいなら、
// このような書き方もできるかも!? 参照:https://qiita.com/mztnnrt/items/f1934e21a318daad71b9
func getPosts(c echo.Context) error {
    ctx := appengine.NewContext(c.Request())
    authorParam := c.Param("author")
    post := &Post{
        Author:  authorParam,
    }
    q := datastore.NewQuery("Post").Filter("author =", post.Author)
    it := q.Run(ctx)
    var posts []Post
    for {
        var user Post
        _, err := it.Next(&user)         // 次のデータを取得
        if err != nil {
            if err == datastore.Done {   // データが無くなったらループを抜ける
                break
            }
            return err                   // エラー発生!
        }
        posts = append(posts, user)      // スライスにデータを追加
    }
    return c.JSON(http.StatusOK, posts)
}

不明点や間違っている点、気軽にコメントしてください。Twitterでも構いません。