はじめに
今回はgoogle大先生の作成した言語であるgo言語(golang)を使って簡単なWebAPIを作成してみました。
ソースコードはこちら
この記事の対象としてはgo言語で手っ取り早くアプリを作ってみたい人、go言語でデータベースとの通信を行うAPIを作成したい人などを想定しています!
参考にした記事
- GoでWebアプリを作ろう 第一回 : Goで簡単なCRUD: http://studio-andy.hatenablog.com/entry/go-todo-crud#f-947194dd
- Golang製DBマイグレーションツールgoose + MySQLを試してみた : https://qiita.com/K_ichi/items/b9362e3a3c5688e494e2
工数
2h * 3 = 6hくらい?
前提条件
- os - macOS(Mojave 10.14.6)
- framework - gin, xo(あとでインストール)
- migration tool - goose(あとでインストール)
- sql - mysql
事前準備
必要に応じて読み飛ばしてください
goのインストール
自分はgoのインストールも済んでいなかったのでhomebrewでgoをインストール
$ brew install go
homebrewが入っていない人はhomebrewをまずインストール
(参考: https://qiita.com/pypypyo14/items/4bf3b8bd511b6e93c9f9)
GOPATHの設定
go言語ではgoのライブラリやモジュールのインストール先の親となるディレクトリをGOPATH
として環境変数に設定しなければならないので設定していきます。
~/goに設定するのがオーソドックスなようなので、自分もホームディレクトリ下にgoというディレクトリを作成し、GOPATH
に設定しました。
...
export GOPATH=/Users/wataru_tajima/go
export PATH=$GOPATH/bin:$PATH
sourceコマンドで変更を反映します。
$ source ~/.bash_profile
プロジェクトの作成
GOPATHという特殊な設定のため、プロジェクトの置き場所に迷う方がいらっしゃるかもしれませんが(自分は迷いました笑)、設定したGOPATH経由でインストールしたモジュールやライブラリをどこからでも参照できるのでプロジェクトの置き場所はどこでも大丈夫です。
プロジェクト構成は以下の通りです。
go_webapi_sample
├── db
│ ├── dbconf.yml
│ └── migrations
│ └── createTask.sql
├── main.go
└── src
├── controller
│ └── task.go
└── model
├── model.go
└── task.go
手順
DBの作成
$ mysql -u <USER_NAME> -p <PASSWORD>
# DBのユーザー作成過程は省きます
mysql> create database <DB_NAME>;
マイグレーションツールのgooseをインストールし、confファイルをコピーして作成します。
$ go get bitbucket.org/liamstask/goose/cmd/goose
go_webapi_sample $ mkdir db
go_webapi_sample $ cd db
go_webapi_sample $ cp $GOPATH/src/bitbucket.org/liamstask/goose/db-sample/dbconf.yml ./
dbconf.ymlのdevelopmentの部分のみ以下のように書き換えます。
# db/dbconf.yml
...
development:
driver: mymysql
open: tcp:localhost:3306*<DB_NAME>/<USER_NAME>/<PASSWORD>
...
疎通の確認(以下のように表示されればOK)
go_webapi_sample $ goose status
goose: status for environment 'development'
Applied At Migration
=======================================
taskテーブルを作成します。
go_webapi_sample $ goose create create_task sql
db/migrations/createTask.sqlを以下のように編集します。
# db/migrations/createTask.sql
-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
CREATE TABLE IF NOT EXISTS task (
id INT UNSIGNED NOT NULL,
created_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
updated_at TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
title VARCHAR(255) NOT NULL,
PRIMARY KEY(id)
);
-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
DROP TABLE task;
modelの作成
それぞれのファイルに以下のように記述していきます。
// src/model/model.go
package model
import (
"database/sql"
"log"
_ "github.com/go-sql-driver/mysql"
)
func DBConnect() (db *sql.DB) {
dbDriver := "mysql"
dbUser := "go_user"
dbPass := "wataru"
dbName := "go_web_app"
dbOption := "?parseTime=true"
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"@/"+dbName+dbOption)
if err != nil {
log.Fatal(err)
}
return db
}
// src/model/task.go
package model
import (
"time"
)
type Task struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Title string `json:"title"`
}
controllerの作成
コントローラーについても同様です。
// src/controller/task.go
package controller
import (
"fmt"
"net/http"
"time"
"../model"
"github.com/gin-gonic/gin"
)
func TasksGET(c *gin.Context) {
db := model.DBConnect()
result, err := db.Query("SELECT * FROM task ORDER BY id DESC")
if err != nil {
panic(err.Error())
}
tasks := []model.Task{}
for result.Next() {
task := model.Task{}
var id uint
var createdAt, updatedAt time.Time
var title string
err = result.Scan(&id, &createdAt, &updatedAt, &title)
if err != nil {
panic(err.Error())
}
task.ID = id
task.CreatedAt = createdAt
task.UpdatedAt = updatedAt
task.Title = title
tasks = append(tasks, task)
}
fmt.Println(tasks)
c.JSON(http.StatusOK, gin.H{"tasks": tasks})
}
今回は読み取り(Read)のみ実装しています。
main.go
// main.go
package main
import (
"./src/controller"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// API namespace
v1 := router.Group("/api/v1")
{
v1.GET("/tasks", controller.TasksGET)
}
router.Run(":8080")
}
これでサーバーを立てることによってlocalhost:8080/api/v1/tasksを叩くことでデータベースに格納されたデータ(タスク一覧)を読み取ることができます。
確認
まずはデータベースにデータを格納します。
mysql> use task;
mysql> insert into task (id, title) values
(1, "todo1"),
(2, "todo2"),
(3, "todo3");
# データの確認
mysql> select * from task;
+----+----------------------------+----------------------------+-------+
| id | created_at | updated_at | title |
+----+----------------------------+----------------------------+-------+
| 1 | 2019-12-26 18:28:57.564010 | 2019-12-26 18:28:57.564010 | todo1 |
| 2 | 2019-12-26 18:29:26.372062 | 2019-12-26 18:29:26.372062 | todo2 |
| 3 | 2019-12-26 18:29:36.072964 | 2019-12-26 18:29:36.072964 | todo3 |
+----+----------------------------+----------------------------+-------+
データが格納されていることが確認できたのでローカルサーバーを建てます。
go_webapi $ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /api/v1/tasks --> _/Users/wataru_tajima/projects/00_self/go_webapi/src/controller.TasksGET (3 handlers)
[GIN-debug] Listening and serving HTTP on :8080
コマンドラインからcurlコマンドでデータを読み取ります。
$ curl localhost:8080/api/v1/tasks | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 343 100 343 0 0 46960 0 --:--:-- --:--:-- --:--:-- 49000
{
"tasks": [
{
"id": 3,
"created_at": "2019-12-26T18:29:36.072964Z",
"updated_at": "2019-12-26T18:29:36.072964Z",
"title": "todo3"
},
{
"id": 2,
"created_at": "2019-12-26T18:29:26.372062Z",
"updated_at": "2019-12-26T18:29:26.372062Z",
"title": "todo2"
},
{
"id": 1,
"created_at": "2019-12-26T18:28:57.56401Z",
"updated_at": "2019-12-26T18:28:57.56401Z",
"title": "todo1"
}
]
}
正しく読み取ることができました〜
今後の展望
読み取りのみではなく追加、編集、削除、絞りこみ等の処理を実装する
ローカルじゃなくてサーバーにあげてみる
最後までご覧いただきありがとうございます!
また次回の記事でお会いしましょ〜🙌