Heroku+echo(go)+MySQLでJSON APIサーバを立ててみる

# はじめに
簡単なAPIサーバを作成したいと思います。
Goを使い始めてまだ浅いので間違っている部分があるかもしれませんがご容赦ください。

# プロジェクトの作成
今回は、`go-api-server`という名前にします。`[username]`は自身の名前で。

```:Terminal
$ mkdir $GOPATH/src/github.com/[username]/go-api-server
$ cd go-api-server
```

# Herokuの準備
Herokuに[登録](https://signup.heroku.com)する。

Herokuのコマンドラインツールを[インストール](https://devcenter.heroku.com/articles/getting-started-with-go#set-up)。
インストールが終わったら、下記コマンドを実行。

```:Terminal
$ heroku login
```

HerokuにNew Appを作成。

```:Terminal
$ heroku apps:create go-api-server --buildpack heroku/go
```

Gitの設定も行います。

```:Terminal
$ git init
$ heroku git:remote -a go-api-server
```

## MySQLの準備
HerokuでMySQLを使うには、クレジットカードの登録が必要。
クレカ登録参考: http://wp.developapp.net/?p=5250

以下のコマンドを使ってClearDBアドオンを追加して、データベースの設定をMySQLに変更。

```:Terminal
$ heroku addons:add cleardb
```

# ファイルの作成

フレームワークは[echo](https://github.com/labstack/echo)、ORMは[gorm](https://github.com/jinzhu/gorm)を使用していきます。
他にも、
- `github.com/go-sql-driver/mysql`
- `github.com/labstack/echo/middleware`
などを使用するので、必要に応じて`go get`しておいてください。

## main.goの作成

環境変数"PORT"を使用するので、osをインポートします。

まだ、dbを宣言していなかったり、Productを作成していないので、エラーになるかもしれませんが、後に作成していきます。

```go:main.go
package main

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

	_ "github.com/go-sql-driver/mysql"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
)

func main() {
	defer db.Close()

	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set")
	}

	e := echo.New()

	// middleware
	e.Use(middleware.Logger())

	// routing
	e.GET("/products", indexProduct)
	e.GET("/products/:id", showProduct)

	e.POST("/products", createProduct)

	e.Logger.Fatal(e.Start(":" + port))
}

func indexProduct(c echo.Context) error {
	var products []Product
	db.Find(&products)
	jsonObject := map[string][]Product{"products": products}
	return c.JSON(http.StatusOK, jsonObject)
}

func showProduct(c echo.Context) error {
	var product Product
	db.First(&product, c.Param("id"))
	return c.JSON(http.StatusOK, product)
}

func createProduct(c echo.Context) error {
	product := Product{Code: "ABCDEF", Price: 1000}
	db.Create(&product)
	return c.JSON(http.StatusOK, product)
}
```

## database.goの作成
データベース設定関係のファイルを作成していきます。
変数datasourceの中身についてはまず、下記コマンドを入力。

```:Terminal
$ heroku config | grep CLEARDB_DATABASE_URL
CLEARDB_DATABASE_URL: mysql://38faf8fefeaaef:dfefe44@us-cdbr-iron-east-093823.cleardb.net/heroku_8cfefedfgcd6a?reconnect=true

// 出力された`CLEARDB_DATABASE_URL`の値を`DATABASE_URL`に格納
$ heroku config:set DATABASE_URL='mysql://38faf8fefeaaef:dfefe44@us-cdbr-iron-east-093823.cleardb.net/heroku_8cfefedfgcd6a?reconnect=true'
```

出力された、CLEARDB_DATABASE_URLの内容

```
mysql://38faf8fefeaaef:dfefe44@us-cdbr-iron-east-093823.cleardb.net/heroku_8cfefedfgcd6a?reconnect=true
```

から、`mysql://`、`?reconnect=true`を取り除く。そして、Host部分を`tcp()`で囲む。また末尾に`?parseTime=true`を追加。

```:変数datasource
38faf8fefeaaef:dfefe44@tcp(us-cdbr-iron-east-093823.cleardb.net:3306)/heroku_8cfefedfgcd6a?parseTime=true
```

```go:database.go
package main

import (
	"fmt"
	"os"

	"github.com/jinzhu/gorm"
)

var db *gorm.DB

func init() {
	var err error
	var datasource string
	if os.Getenv("DATABASE_URL") != "" {
        // Heroku用
　　　　　　　　　　　　　　　datasource = "[username]:[password]@tcp([host]:3306)/[database]?parseTime=true"
	} else {
        // ローカル用
		datasource = "[username]:[password]@/[database]?parseTime=true"
	}
	db, err = gorm.Open("mysql", datasource)
	if err != nil {
		panic("failed to connect database")
	}

	db.AutoMigrate(&Product{})
}

```

## product.goの作成
Productモデルを定義していきます。

gorm.Modelには、`ID`、`CreatedAt`などの基本的な情報が定義されています。

```go:product.go
package main

import "github.com/jinzhu/gorm"

type Product struct {
	gorm.Model
	Code  string
	Price uint
}
```

## Procfileの作成
Herokuでは、`Procfile`というファイルが必要らしいので追加します。

```:Procfile
web: go-api-server
```

## 各依存パッケージ管理方法
今回は、`govendor`というツールを使用します。

```:Terminal
// インストール
$ go get -u github.com/kardianos/govendor

// 初期化
$ govendor init

// 設定ファイルの更新と依存パッケージの vendor へのダウンロード
$ govendor fetch +out
```

# デプロイ
```:Terminal
$ git add .
$ git commit -m "comment"
$ git push heroku master
```

デプロイされたか確認します。

```:Terminal
$ heroku open products
```

確認できれば完成です。
おそらく初めて立ち上げたときには、まだデータがないので、以下のようなjsonが帰ってくると思います。

```json:json
{
   products: []
}
```

/productsにPOSTするとデータが増えていきます。
ただ、IDが10ずつインクリメントされていきます。
参考記事: [herokuでmysql(ClearDB)を使うとidが10ずつ増える](http://qiita.com/nsatohiro/items/0458e63c47c3d6ff37d0)

# 終わりに
久しぶりにHerokuを使いましたが便利ですね。
もっとGoを使いこなせるようになりたいです。

# 参考文献
- [Getting Started on Heroku with Go](https://devcenter.heroku.com/articles/getting-started-with-go#introduction)
- [ClearDB MySQL](https://devcenter.heroku.com/articles/cleardb)
- [HerokuでGoアプリケーションを動かしたい](http://kwmt27.net/2017/03/23/how-to-deploy-go-application-on-heroku/)
- [Heroku への Go 言語製アプリケーションのデプロイと依存パッケージ管理方法の比較] (http://qiita.com/nirasan/items/32f2e0fc94ccf123ca55)
- [labstack/echo](https://github.com/labstack/echo)
- [jinzhu/gorm](https://github.com/jinzhu/gorm)