【Golang + echo】GoとechoでSQLのデータを表示【PostgreSQL】

# 1.はじめに
記事をご覧いただきありがとうございます。

今までC#とMySQLの接続は行ったことがあったのですが
ほとんど触れたことのないGo言語(+echo)とPostgreSQLとの接続
を行うことになったので備忘録的に手順を記していこうかと思います。

初歩的なところから説明するので適宜読み飛ばしていただければと思います。

# 2.PostgreSQLの設定
## 2-1.PostgreSQLのダウンロード
[PostgreSQLのHP](https://www.postgresql.org/)にアクセスしてダウンロードをクリック


<img width="1085" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/4f888c13-50d6-3b0e-4b81-da40484ccf08.png">


Binary packagesの中から自分のOSをクリック


<img width="902" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/b46a5601-0916-709d-0b97-d00c783f50bf.png">



ページ内の**Download the installer**をクリック(今回はWindowsですが他も同じ手順です。)


<img width="892" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/7053e05c-6e95-2678-a4fa-e2eb29970aaa.png">


自分のOSの使いたいバージョンの**Download**をクリック
(今回はWindowsx86-64の12.1バージョンをダウンロードしました。)


<img width="890" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/04016bc6-bd59-4ff4-5952-57322f35683e.png">


## 2-2.PostgreSQLのインストールと環境変数設定(Windows10)
ダウンロードした**postgresql-(バージョン数)-(OS名)**アプリケーションを実行し
このような画面が出たら基本的に**Next>**を押し続けてください。
**password**だけは自分で設定しますが、**port**は基本的に干渉しなければ変えなくてよいと思います


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/806add3c-0a71-16ed-dc70-c83593098c98.png)


インストールが完了したらシステム環境変数を設定します。
PostgreSQLをインストールした場所の中のbinフォルダがある場所
(変更していなければ、**C:\Program Files\PostgreSQL\(バージョン数)**)
にPathを通します。

Windows10の場合、スタートメニューを右クリック＞設定で以下の画面が出るので
「システム環境」と入力すると「**システム環境変数**」が出てきます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/c890a200-f2c3-4068-3056-72eceeada06f.png)


下のような画面が出てくるので環境変数をクリック


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/f39a053a-f355-c96e-694c-8fac334e88ba.png)


システム環境変数のPathをクリックして編集＞前のパスの間に**;(セミコロン)**を
はさんでPostgreSQLをインストールした場所の中のbinフォルダがある場所のパスを入力


<img width="441" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/5f460a3c-32b4-116e-a775-c81641fca300.png">

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/54143199-919f-7d3a-fd98-1afa63971ea9.png)


ここまで終わったらコマンドプロンプトを起動し、

```
psql --varsion
```

と入力してPostgreSQLのバージョン数が出ればインストール完了です。


# 3.echoのインストール

下のコマンドで一発でインストールできます。

```
go get -u github.com/labstack/echo...
```

# 4.SQLのデータをc.Stringでreturnするプログラム
## 4-1.概要
PostgreSQLの表示パターンを2個作っていきます。

* sql.GetPost()

URLのプレースホルダからidをもらってきて
idが一致するレコードを表示する。

* sql.GetPosts()

**posts**テーブルのレコードをすべて表示する。


## 4-2.データベースとテーブル作成

コマンドプロンプトでPostgreSQLにログインする
下のコマンドを打ってパスワードを入力

```
psql -U postgres
```

今回は**sample**という名前のDBを作るので
下のコマンドを入力

```
CREATE DATABASE sample
```

先ほど作ったデータベースに接続するコマンドを入力

```
¥c sample
```

**sample**データベースに**posts**テーブルを作る

```
create table posts (
  id integer, 
  content varchar(80),
  content author(80)
);
```

**posts**テーブルにレコード挿入する(お好きなだけどうぞ)

```
insert into posts values (1, 'test', 'test1user');
insert into posts values (2, 'testtest', 'test2user');
insert into posts values (3, 'testtesttest', 'test3user');
```

以上でPostgreSQLの操作は完了です。

## 4-3.フォルダ構成
```
作業フォルダ--- main.go
             ∟ sql      --- sql.go
```

## 4-4.main.go
```Go
package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "github.com/~/sql"   //SQLについて記述したパッケージのパス
)

func main() {
    // Echoのインスタンス
    e := echo.New()

    // ミドルウェア類
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(interceptor.BasicAuth())

    // ルーティング
    e.GET("/sql/record/:id", sql.GetPost()) //プレースホルダでidをもらってくる
    e.GET("/sql/table", sql.GetPosts())

    // サーバー起動
    e.Start(":1323")
}
```

【ポイント】
* 基本的にmain.goにはルーティングをだけをしています。

## 4-5.sql.go
```Go
package sql

import (
	"database/sql"
	"net/http"
	"github.com/labstack/echo"
	"github.com/pkg/errors"
	_ "github.com/lib/pq"
	"strconv"
)

type Post struct {
	Id      int
	Content string
	Author  string
}

var content string

var Db *sql.DB

func init() {
	var err error
	Db, err = sql.Open("postgres", "user=postgres dbname=sample password=自分で設定したパスワード sslmode=disable")
	if err != nil {
		panic(err)
	}
}

func GetPost() echo.HandlerFunc {
	return func(c echo.Context) error {
		id := c.Param("id")  //プレースホルダのidを取得
		post := Post{}
		if err := Db.QueryRow("select id, content, author from posts where id = $1", id).Scan(&post.Id, &post.Content, &post.Author); err != nil {
			return errors.Wrapf(err, "connot connect SQL")
		}
		return c.String(http.StatusOK, " ID : " + strconv.Itoa(post.Id) + " CONTENT : " + post.Content + " AUTHOR : " + post.Author)
		// string(Post.Id)だと何故かファイルダウンロードしてしまい、IDも表示されない。
		// strconv.Itoa(post.Id) の部分はプレースホルダから取り出したidでも可
	}
}

func GetPosts() echo.HandlerFunc {
	return func(c echo.Context) error {
		post := Post{}
		response := ""

		rows, err := Db.Query("select id, content, author from posts")
		if err != nil {
			return errors.Wrapf(err, "connot connect SQL")
		}
		defer rows.Close()

		for rows.Next(){
			if err := rows.Scan(&post.Id, &post.Content, &post.Author); err != nil {
				return errors.Wrapf(err, "connot connect SQL")
			}
			response += "ID : " + strconv.Itoa(post.Id) + "	CONTENT : " + post.Content + "	AUTHOR : " + post.Author + "\n"
		}

		return c.String(http.StatusOK, str)

	}
}

```

【ポイント】
* Db.QueryRowは1つのレコードを取得する関数でDb.Queryは複数のレコードを取得する関数
* post.Idに関してはint型なのでstring(Post.Id)にキャストしようと思うと思いますが
　それだと変な挙動をしてしまうので、**strconv**をインポートして**strconv.Itoa(post.Id)**とすることできちんと動きます。
* GetPosts()のほうは何回もreturnすることができないのでresponseという文字列にすべての結果を格納して表示しています。

## 4-6.実行画面

* sql.GetPost()
<img width="562" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/e60564a7-6ce4-9870-1051-223f3be70597.png">

* sql.GetPosts()
<img width="562" alt="無題.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/09802c68-2169-a015-184a-3732f3999263.png">


# 5.SQLのデータをc.JSONでreturnするプログラム(12.12 追記)

記事を投稿した後にAPIサーバで使うならc.JSONで返すプログラムでなければならないな…
と思いc.JSONでreturnするプログラムに作り変えてみました。

main.goは変更していないので4-4をご覧ください。

## 5-1.sql.go
```Go
package sql

import (
	"database/sql"
	"net/http"
	"github.com/labstack/echo"
	"github.com/pkg/errors"
	_ "github.com/lib/pq"
)

type Post struct {
	Id      int	`json:"id"`
	Content string	`json:"content"`
	Author  string	`json:"author"`
}

var content string

var Db *sql.DB

func init() {
	var err error
	Db, err = sql.Open("postgres", "user=postgres dbname=sample password=自分で設定したパスワード sslmode=disable")
	if err != nil {
		panic(err)
	}
}

func GetPost() echo.HandlerFunc {
	return func(c echo.Context) error {
		id := c.Param("id")
		post := Post{}
		posts := []*Post{}

		if err := Db.QueryRow("select id, content, author from posts where id = $1", id).Scan(&post.Id, &post.Content, &post.Author); err != nil {
			return errors.Wrapf(err, "connot connect SQL")
		}

		posts = append(posts, &Post{Id: post.Id, Content: post.Content, Author: post.Author})
		return c.JSON(http.StatusOK, posts)
	}
}

func GetPosts() echo.HandlerFunc {
	return func(c echo.Context) error {
		post := Post{}
		posts := []*Post{}

		rows, err := Db.Query("select id, content, author from posts")
		if err != nil {
			return errors.Wrapf(err, "connot connect SQL")
		}
		defer rows.Close()

		for rows.Next(){
			if err := rows.Scan(&post.Id, &post.Content, &post.Author); err != nil {
				return errors.Wrapf(err, "connot connect SQL")
			}
			posts = append(posts, &Post{Id: post.Id, Content: post.Content, Author: post.Author})
		}

		return c.JSON(http.StatusOK, posts)

	}
}

```
【ポイント】
* structに`json:"○○"`を追加する
* postsにデータをappendしてからreturn

## 5-2.実行例

* sql.GetPost()
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/6c3c86d9-5e24-001a-f2cc-192f7709e30e.png)

* sql.GetPosts()
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486705/a497c3a6-504d-aa61-f5d7-a01485e58f3b.png)



# 6.終わりに

GoもechoもPostgreSQLも初心者なのでざっとした流れとSQLのデータ表示についてまとめてみました。
まとめてくれているサイトなどがかなり少なかったので同じようなことに迷っている人の
助けになれば幸いです。

何か間違っているところや改善したほうがいいところなどありましたら教えていただけると幸いです。

# 7.参考文献
* [Go の echo ってWebサーバーでサクッと REST しちゃう](https://qiita.com/ezaki/items/62e806ae42828bb3567a)
* [【golang】postgresqlを使う](https://qiita.com/mztnnrt/items/8f671b39269b68e9ef5d)
* [GolangとEchoでお手軽にAPIサーバを立てる](https://ken-aio.github.io/post/2019/01/30/golang-echo/)