Skip to content

プロジェクトのセットアップ

環境準備

今回の演習は、(第一部)サーバーからデータベースを扱う の状態から開始します。

もしファイルを削除してしまった場合は、以下の手順でセットアップしましょう。

  1. データベースを扱う準備 からプロジェクトをセットアップしましょう。

  2. .env ファイルを作成し、以下のように編集しましょう。

sh
DB_USERNAME="root"
DB_PASSWORD="password"
DB_HOSTNAME="localhost"
DB_PORT="3306"
DB_DATABASE="world"
  1. go mod tidy を実行しましょう。

以上でセットアップはできているはずです。

ファイルの分割

このまま演習を始めてしまうとファイルが長くなりすぎてしまうので、ファイルを別のパッケージとして分割します。

TIP

パッケージとは、関連する複数のファイルをまとめる単位のことです。
ディレクトリとパッケージは一対一に対応しています。原則的に、ディレクトリ名とパッケージ名は同じにします。
パッケージによって、機能を分離でき、変数や関数の公開範囲を最低限にできる等沢山の恩恵が得られます。
パッケージの外部に公開する変数や関数などのシンボルは、先頭を大文字にする必要があります。
逆に言えば、先頭が大文字でないシンボルは、パッケージの外部からはアクセスできません。
詳しくは以下を参照してください。 A Tour of Go - Packages
Effective Go - package-names

各エンドポイントでの処理はハンドラーと呼ばれますが、それを handler/handler.go に移動してみましょう。手順は以下の通りです。

handler.go の作成

  1. handler というディレクトリを新しく作成し、その中に handler.go というファイルを作成する。
  2. handler.go を以下のように記述する。
go
package handler

import (
	"database/sql"
	"errors"
	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo/v4"
	"log"
	"net/http"
)

type Handler struct {
	db *sqlx.DB
}

func NewHandler(db *sqlx.DB) *Handler {
	return &Handler{db: db}
}

type City struct {
	ID          int            `json:"id,omitempty"  db:"ID"`
	Name        sql.NullString `json:"name,omitempty"  db:"Name"`
	CountryCode sql.NullString `json:"countryCode,omitempty"  db:"CountryCode"`
	District    sql.NullString `json:"district,omitempty"  db:"District"`
	Population  sql.NullInt64  `json:"population,omitempty"  db:"Population"`
}

func (h *Handler) GetCityInfoHandler(c echo.Context) error {
	cityName := c.Param("cityName")

	var city City
	err := h.db.Get(&city, "SELECT * FROM city WHERE Name=?", cityName)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return c.NoContent(http.StatusNotFound)
		}
		log.Printf("failed to get city data: %s\n", err)
		return c.NoContent(http.StatusInternalServerError)
	}

	return c.JSON(http.StatusOK, city)
}

func (h *Handler) PostCityHandler(c echo.Context) error {
	var city City
	err := c.Bind(&city)
	if err != nil {
		return echo.NewHTTPError(http.StatusBadRequest, "bad request body")
	}

	result, err := h.db.Exec("INSERT INTO city (Name, CountryCode, District, Population) VALUES (?, ?, ?, ?)", city.Name, city.CountryCode, city.District, city.Population)
	if err != nil {
		log.Printf("failed to insert city data: %s\n", err)
		return c.NoContent(http.StatusInternalServerError)
	}

	id, err := result.LastInsertId()
	if err != nil {
		log.Printf("failed to get last insert id: %s\n", err)
		return c.NoContent(http.StatusInternalServerError)
	}
	city.ID = int(id)

	return c.JSON(http.StatusCreated, city)
}

ファイルを編集したら、go mod tidy を実行しましょう。

main.go の編集

main.goを以下のように編集しましょう。

go
package main

import (
	"github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/joho/godotenv"
	"github.com/labstack/echo/v4"
	"github.com/traPtitech/naro-template-backend/handler"
	"log"
	"os"
	"time"
)

func main() {
	// .envファイルから環境変数を読み込み
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatal(err)
	}

	// データーベースの設定
	jst, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		log.Fatal(err)
	}
	conf := mysql.Config{
		User:      os.Getenv("DB_USERNAME"),
		Passwd:    os.Getenv("DB_PASSWORD"),
		Net:       "tcp",
		Addr:      os.Getenv("DB_HOSTNAME") + ":" + os.Getenv("DB_PORT"),
		DBName:    os.Getenv("DB_DATABASE"),
		ParseTime: true,
		Collation: "utf8mb4_unicode_ci",
		Loc:       jst,
	}

	// データベースに接続
	db, err := sqlx.Open("mysql", conf.FormatDSN())
	if err != nil {
		log.Fatal(err)
	}

	h := handler.NewHandler(db)
	e := echo.New()

	e.GET("/cities/:cityName", h.GetCityInfoHandler)
	e.POST("/cities", h.PostCityHandler)

	err = e.Start(":8080")
	if err != nil {
		log.Fatal(err)
	}
}

ファイルを編集したら、go mod tidy を実行しましょう。
ここまで出来たら、画像のようになっているはずです。

準備完了

ファイルの分割で変更したのは、以下の 3 点です。

  1. handlerパッケージを作成し、コードを分割した。
  2. handlerというdbをフィールドに持つ構造体を作成し、その構造体のメソッドとしてGetCityInfoHandlerPostCityHandlerを定義した。
  3. .envファイルの環境変数を、プログラムで読むようにした。

それでは、go run main.go で実行してみましょう。

TIP

main packageを複数ファイルに分割した場合、go run main.goだとmain.goのみがビルドされるため、
go run .go run main1.go main2.goのようにして複数ファイルを読み込む必要があります。
詳しくはgo help runを参照してください。

無事起動が出来たら、ターミナルでtask upを実行してデーターベースを起動し、localhost:8080/cities/Tokyoにアクセスして実際に動いていることを確認しましょう。

上手く動いていることを確認できたら、 Ctrl+C で一旦止めましょう。