Go言語でRESTful APIを実装する（＋Go言語の基本を理解する）

Go言語の学習目的で簡単なRESTful APIを実装した。無知識の状態からのスタートであったが、学習・実装の過程や、理解に役に立ったドキュメントを紹介する。

### (実装したRESTful API サンプルプロジェクト)

データ永続化や細かいエラー処理には対応していないざっくり実装ではあるが、GitHubにソースコードを載せている。

https://github.com/unhurried/go-rest-api-example/

### Go言語の基本を理解する

[Go言語のGitHub Wiki](https://github.com/golang/go/wiki#getting-started-with-go)に良質なドキュメントが紹介されている。

* [A Tour of Go](https://tour.golang.org/list)
* [Effective Go](https://golang.org/doc/effective_go.html)

その他では以下のドキュメントが役に立った。

* [Go cheatsheet](https://devhints.io/go)

### 開発環境を構築する (Windows)

1つの開発環境で複数のGo言語のバージョンを簡単に切り替えられるようにするツールとしては[goenv](https://github.com/syndbg/goenv)もしくは[gvm](https://github.com/moovweb/gvm)が人気がある。（[参考](https://hodalog.com/research-a-version-manager-of-go/)）

残念ながらどちらもChocolateyのパッケージは存在しないため、素直に[golang](https://community.chocolatey.org/packages/golang)パッケージをインストールする。

```
choco install golang
```

IDEにはVisual Code Studioを利用する。Go言語の公式プラグイン（[golang.go](https://marketplace.visualstudio.com/items?itemName=golang.go)）をインストールすれば、コードナビゲーション・補完やビルド・デバッグなど一通りの必要な機能が利用できる。

### 調べたこと（参考になったドキュメント）

#### プロジェクトの構成

Google検索から入ると「GOPATHをどうのこうの…」といった記事が多く引っかかるが、Go 1.16ではGo Moduleが推奨となったため、難しいことは考えなくてOKとのこと。

参考：https://zenn.dev/nobonobo/articles/4fb018a24f9ee9

#### パッケージや変数名の命名規則

[Effective Go - Names](https://golang.org/doc/effective_go#names)

* パッケージ名は小文字で1単語。ディレクトリのベース名と合わせる。例：`bytes`
* GetterはGetなしのフィールド名、SetterはSet+フィールド名。例：`Name`, `SetName`
* 1メソッドしか持たないインターフェースはメソッド名+erとする。例：`Reader`
* 複数単語は`MixedCaps`もしくは`mixedCaps`とする。

#### mapの使い方

[Go maps in action](https://blog.golang.org/maps)

```go
myMap := map[string]int{"a": 1, "b": 2}
fmt.Print(myMap["a"])
```

#### ユニークな識別子を生成する

人気どころのライブラリが[Generating good unique ids in Go](https://blog.kowalczyk.info/article/JyRZ/generating-good-unique-ids-in-go.html)で紹介されている。今回は[rx/xid](rx/xid)を使うこととした。

#### RESTful APIを実装するためのライブラリ

ルーティングやパスパラメータの処理用ライブラリとしては[gorilla/mux](gorilla/mux)もしくは[gin-gonic/gin](https://github.com/gin-gonic/gin)が人気のようである。（[参考](https://forum.golangbridge.org/t/is-gorilla-mux-a-mainly-used-package-to-write-restful-api/7089)）
今回はえいやでgorilla/mux](gorilla/mux)を使うことに決めた。以下ではgorilla/muxに関するTipsを記載する。

##### Subrouterを使ってルーティング処理を分割する

`mux.Router.PathPrefix("xxx").Subrouter()` でSubrouterを生成して、各パッケージでルーティング設定を記述すればよい。

実装例：[app.go](https://github.com/unhurried/go-rest-api-example/blob/master/app.go#L18)、[todo/controller.go](todo/controller.go)

##### 共通処理をMiddlewareとして括りだす

レスポンスヘッダの設定やログ出力など全てのAPIで共通化する処理は[Middleware](https://github.com/gorilla/mux#middleware)として実装すればよい。特定のリソースのみの共通処理の場合いはSubrouterに対してMiddlewareを設定すればよい。

実装例：[app.go](https://github.com/unhurried/go-rest-api-example/blob/master/app.go#L35)

##### Trailing Slashありなしを同列に扱う

gorilla/muxでのTrailing Slashあり・なしの扱いは、301でのリダイレクトもしくは別のルートとして扱う、のどちらか（[StrictSlash](https://pkg.go.dev/github.com/gorilla/mux#Router.StrictSlash)）しかできない。それならと[Middleware](https://github.com/gorilla/mux#middleware)を作って自力でTrailing Slashを除去しようとすると、gorilla/muxのMiddlewareはルーティング成功時にしか発火しない。ということで、[net/http](net/http)の方のMiddlewareとして定義することとなる。

実装例：[app.go](https://github.com/unhurried/go-rest-api-example/blob/master/app.go#L20)

参考

* https://www.husainalshehhi.com/blog/gorilla-mux-trailing-slashes/
* https://github.com/matrix-org/dendrite/issues/613

### 積み残し（今後やること）

* データベース（RDBMS, NoSQL）を利用したデータ永続化（O/R Mapper）
* リクエスト・レスポンスボディのシリアライズ・デシリアライズの共通化
* エラー処理をきちんと書く（エラー処理の共通化）
* ポインタレシーバやインターフェースを利用して、オブジェクト指向っぽい実装にする