スマレジエンジニアyushiのブログ

スマレジエンジニアのブログ

【TODOリスト 第5回】一覧・作成API

引き続きTODOリストを作っていきます。

yushi-dev.hatenablog.com

今回は、一覧APIと作成APIを追加します。

下記の書籍を参考にさせていただいています。

ルーティング

/taskへのリクエストを、それぞれ対応するハンドラメソッドに渡します。

func main() {
    ...
    http.HandleFunc("/tasks", handleRequest)
    ...
}


func handleRequest(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
    w.Header().Set("Content-Type", "application/json")
    var err error
    switch r.Method {
    case "GET":
        err = handleGetAll(w, r)
    case "POST":
        err = handleStore(w, r)
    }
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
}

Method(GET/POST)で分岐しています。
GO言語標準だとどうにもこういう泥臭い書き方になる模様です。HTTPフレームワークを入れたい...。
Access-Control-Allow-Originヘッダーの指定によって、フロントエンドにおけるCORSエラーを回避しています。

Jsonエンコーディング

encoding/jsonという公式のJson処理用ライブラリを導入します。

structにアノテーションをつけて、jsonと相互変換した時のkey名を指定します。

type Task struct {
    Id     int    `json:"id"`
    Name   string `json:"name"`
    Status int    `json:"status"`
}

一覧取得API

DBから取得し、ループ処理でstructに値を入れます。

func getTasks(limit int) (tasks []Task, err error) {
    rows, err := db.Query("SELECT id, name, status FROM tasks LIMIT ?", limit)
    defer rows.Close()

    if err != nil {
        fmt.Println(err.Error())
        return
    }

    for rows.Next() {
        task := Task{}
        if err = rows.Scan(&task.Id, &task.Name, &task.Status); err != nil {
            return
        }
        tasks = append(tasks, task)
    }
    return
}

関数型っぽいmapとかは標準であるんでしょうか?
foreachは、気分が良くはないですね。

jsonに変換して、レスポンスを返します。

func handleGetAll(w http.ResponseWriter, r *http.Request) (err error) {
    tasks, err := getTasks(30)
    json, _ := json.Marshal(tasks)
    w.Write(json)
    return
}

作成API

リクエストのパース用にstructを用意します。

type StoreRequest struct {
    Name string `json:"name"`
}

リクエストをjson decodeし、DBにINSERTします。

func storeTask(name string) (err error) {
    _, err = db.Exec("INSERT INTO tasks(name, status) VALUES (?, 0)", name)
    return
}

func handleStore(w http.ResponseWriter, r *http.Request) (err error) {
    var sr StoreRequest
    json.NewDecoder(r.Body).Decode(&sr)
    err = storeTask(sr.Name)
    return
}

フロントエンドのAPI連携

一覧API連携部分は、vueと独立したjsモジュールとして用意します。
Fetch APIを利用しています。

export function getAll() {
  return fetch('http://localhost:8000/tasks');
}

export function store(data) {
  return fetch('http://localhost:8000/tasks', {
    method: 'POST',
    body: JSON.stringify(data),
  })
} 

あとは、前回作成したモックをこのモジュールを使うように書き直します。

まとめ

さくさくAPIを追加しました。
Goはあまりスマートに記述できている気がしていないですが...。
ルーティングを書くのが面倒になりそうなので、フレームワークを導入する予定です。