概要
dockerを使用してGo言語でJSONを返すAPIサーバーを構築
(+ローカルで起動させたreactからAPIを呼び出しJSONを受け取る)
※dockerとgolangの動作環境が準備済みである事、reactの環境構築については割愛する
目次
1. dockerコンテナとgo言語環境ソースファイルのファイル構成
2. dockerコンテナの準備
3. APIサーバーの構築
4. react側の記述
1. dockerコンテナとgo言語環境ソースファイルのファイル構成
go言語の環境構築については下記記事参照
windowsでGo言語を使ったWEBアプリケーション作成の環境を構築する
docker
+---golang-api
| docker-compose.yml
+---images
+---golang-api
DockerFile
go
+---sample-app
| go.mod
| go.sum
| main.go
+---sql
sql.go
ローカルで作成したgo言語環境をdockerコンテナにバインドさせてAPIサーバーを作成する
2. dockerコンテナの準備
docker-compose.ymlとDockerFileを下記のように記述する
dockerのベースイメージはgolang公式の1.19.3-alpineを使用
docker-compose.ymlのあるディレクトリで「docker-compose up --build -d」を実行すればコンテナ起動する
version: '3'
services:
# サービス名
golang-api:
# コンテナ名
container_name: golang-api-container_test
# Dockerfileのあるディレクトリを指定
build: /images/golang-api
# ローカルのフォルダをコンテナのフォルダと同期させる
volumes:
- type: bind
source: ../../go/sample-app
target: /go/src/app
# ホストの127.0.0.1:1323からコンテナの1323に繋ぐ
ports:
- "127.0.0.1:1323:1323"
FROM golang:1.19.3-alpine
# ROOTフォルダ及びWORKDIR設定
ENV ROOT=/go/src/app
RUN mkdir ${ROOT}
WORKDIR ${ROOT}
# パッケージの更新とビルドツールパッケージのインストール
RUN apk update && apk add git alpine-sdk
# 環境変数設定
ENV CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64
# port設定
EXPOSE 8080
# コンテナ起動時にgoを起動
CMD ["go", "run", "main.go"]
3. APIサーバーの構築
main.goを下記のように記述する
middleware.CORSWithConfigでCORS(オリジン間リソース共有)の設定を行う
AllowOrigins:の記述でlocalhost:3000で動作するフロントエンドのreactサーバーからの接続を許可している(全て許可する場合は「"*"」にする)
package main
import (
/* sql実行用のsqlパッケージをインポート */
"sample-app/sql"
/* サーバー起動用のパッケージ */
"net/http"
/* ミドルウェア用パッケージ */
"github.com/labstack/echo/middleware"
/* メインのフレームワークにechoを使用 */
"github.com/labstack/echo"
)
func main() {
/* echoのインスタンス作成 */
e := echo.New()
/* ミドルウェア類 */
e.Use(middleware.Logger())
e.Use(middleware.Recover())
/* CORS(オリジン間リソース共有)の設定 */
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
/* http://localhost:3000からの接続を許可する */
AllowOrigins: []string{"http://localhost:3000"},
AllowMethods: []string{
http.MethodGet,
http.MethodPut,
http.MethodPost,
http.MethodDelete,
},
}))
/* パスでのリクエストに対する処理 */
e.GET("/path/:no", sql.GetFromPath)
/* クエリでのリクエストに対する処理 */
e.GET("/query", sql.GetFromQuery)
/* JSONでのリクエストに対する処理 */
e.POST("/json", sql.PostFromJson)
/* サーバー起動 */
e.Logger.Fatal(e.Start(":1323"))
}
DBへの接続及び処理をsql.goに記述する(ドライバはlib/pqを使用)
sql.Openのhost(IP),port,user,password,dbnameは各自設定する事
package sql
import (
/* データベース操作用パッケージ */
"database/sql"
/* サーバー起動用のパッケージ */
"net/http"
/* 文字列型と数値型・論理型の相互変換用パッケージ */
"strconv"
/* メインのフレームワークにechoを使用 */
"github.com/labstack/echo"
/* データベース接続用のドライバ */
_ "github.com/lib/pq"
)
/* パスでのリクエストに対してのreturn用json */
type ReturnFromPath struct {
Result int `json:"result"`
Name string `json:"name"`
}
/* クエリでのリクエストに対してのreturn用json */
type ReturnFromQuery struct {
Result int `json:"result"`
No int `json:"no"`
DtlNo int `json:"dtl_no"`
Name string `json:"name"`
}
/* jsonでのリクエストに対してのreturn用json */
type ReturnFromJson struct {
Result int `json:"result"`
No int `json:"no"`
DtlNo int `json:"dtl_no"`
Name string `json:"name"`
}
/* データベースへのselectに対する結果を取得する用 */
type ResultTestTable struct {
No int
DtlNo int
Name string
}
/* JSONでのリクエストを受け取る用 */
type PostTest struct {
No int `json:"no"`
DtlNo int `json:"dtl_no"`
}
var Db *sql.DB
/* 初期化処理、initは他のfuncが呼び出された際も最初に実行される */
func init() {
var err error
/* DB接続設定 */
Db, err = sql.Open("postgres", "host=**** port=**** user=**** password=**** dbname=**** sslmode=disable")
if err != nil {
panic(err)
}
}
/* パスでのリクエストに対する処理 */
func GetFromPath(c echo.Context) error {
/* Paramでパスの値を取得する */
no := c.Param("no")
/* selectの結果取得用 */
result := ResultTestTable{}
/* return用のJSON */
returnJson := ReturnFromPath{}
/* select実行 */
if err := Db.QueryRow("select no, name from api_test_table where no = $1", no).Scan(&result.No, &result.Name); err != nil {
returnJson.Result = 2
} else {
returnJson.Result = 1
returnJson.Name = result.Name
}
/* jsonを返す */
return c.JSON(http.StatusCreated, returnJson)
}
/* クエリでのリクエストに対する処理 */
func GetFromQuery(c echo.Context) error {
/* QueryParamでクエリの値を取得する */
no := c.QueryParam("no")
dtl_no := c.QueryParam("dtl_no")
/* selectの結果取得用 */
result := ResultTestTable{}
/* return用のJSON */
returnJson := ReturnFromQuery{}
/* select実行 */
if err := Db.QueryRow("select no, dtl_no, name from api_test_table where no = $1 and dtl_no = $2", no, dtl_no).Scan(&result.No, &result.DtlNo, &result.Name); err != nil {
returnJson.Result = 2
} else {
returnJson.Result = 1
returnJson.No = result.No
returnJson.DtlNo = result.DtlNo
returnJson.Name = result.Name
}
/* jsonを返す */
return c.JSON(http.StatusCreated, returnJson)
}
/* jsonでのリクエストに対する処理 */
func PostFromJson(c echo.Context) error {
/* データ受け取り用のjsonを定義 */
post := new(PostTest)
/* Bindでデータを受け取る */
if err := c.Bind(post); err != nil {
return err
}
/* selectの結果取得用 */
result := ResultTestTable{}
/* return用のJSON */
returnJson := ReturnFromJson{}
/* select実行 */
if err := Db.QueryRow("select no, dtl_no, name from api_test_table where no = $1 and dtl_no = $2", strconv.Itoa(post.No), strconv.Itoa(post.DtlNo)).Scan(&result.No, &result.DtlNo, &result.Name); err != nil {
returnJson.Result = 2
} else {
returnJson.Result = 1
returnJson.No = result.No
returnJson.DtlNo = result.DtlNo
returnJson.Name = result.Name
}
/* jsonを返す */
return c.JSON(http.StatusCreated, returnJson)
}
GetFromPathはパス指定でのGETリクエストに対する処理を行っている
(http://127.0.0.1:1323/path/1での呼び出し)
GetFromQueryはクエリでのGETリクエストに対する処理を行っている
(http://127.0.0.1:1323/query?no=2&del_no=1での呼び出し)
PostFromJsonはJSONでのPOSTリクエストに対する処理を行っている
(http://127.0.0.1:1323/jsonでの呼び出し)
4. react側の記述
reactサーバーの構築については割愛
参照:Node.js+React+Herokuでアプリ開発環境を構築する
パス、クエリ、JSONでのfetchを使用した呼び出し方法は下記の通り
requestOptionsでmode: 'cors'を指定する必要があるので注意
/* API接続設定 */
const requestOptions = {
mode: 'cors',
method: 'GET'
};
/* 外部APIへ接続 */
fetch('http://127.0.0.1:1323/path/1', requestOptions)
/* APIからのデータをJSONに変換 */
.then((res) => res.json())
.then(
(data) => {
/* API正常終了 */
if(data.result===1) {
alert(data.name);
setLoading(false);
/* APIエラー */
}else if(data.result===2) {
alert('エラー。');
setLoading(false);
}
}
);
/* API接続設定 */
const requestOptions = {
mode: 'cors',
method: 'GET'
};
/* 外部APIへ接続 */
fetch('http://127.0.0.1:1323/query?no=2&dtl_no=1', requestOptions)
/* APIからのデータをJSONに変換 */
.then((res) => res.json())
.then(
(data) => {
/* API正常終了 */
if(data.result===1) {
alert(data.name);
setLoading(false);
/* APIエラー */
}else if(data.result===2) {
alert('エラー。');
setLoading(false);
}
}
);
/* API接続設定 */
const sendData = {
no: 2,
dtl_no: 2
}
const requestOptions = {
mode: 'cors',
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(sendData)
};
/* 外部APIへ接続 */
fetch('http://127.0.0.1:1323/json', requestOptions)
/* APIからのデータをJSONに変換 */
.then((res) => res.json())
.then(
(data) => {
/* API正常終了 */
if(data.result===1) {
alert(data.name);
setLoading(false);
/* APIエラー */
}else if(data.result===2) {
alert('エラー。');
setLoading(false);
}
}
);