LoginSignup
3
3

More than 1 year has passed since last update.

go言語(echo)APIサーバーの構築手順(+reactとの接続設定手順)

Last updated at Posted at 2022-12-07

概要

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」を実行すればコンテナ起動する

docker-compose.yml
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"
Dockerfile
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サーバーからの接続を許可している(全て許可する場合は「"*"」にする)

main.go
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は各自設定する事

sql.go
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'を指定する必要があるので注意

App.js(パス指定でのGETリクエスト)
    /* 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);
          }
        }
      );
App.js(クエリでのGETリクエスト)
    /* 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);
          }
        }
      );
App.js(JSONでのPOSTリクエスト)
    /* 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);
          }
        }
      );
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3