Skip to content

サーバーからデータベースを扱う

Echo を使い、データベースからデータを取得するサーバーアプリケーションを作りましょう。

go
package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo/v4"
)

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

var (
	db *sqlx.DB
)

func main() {
	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)
	}
	log.Println("connected")
	db = _db

	e := echo.New()

	e.GET("/cities/:cityName", getCityInfoHandler)

	e.Start(":8080")
}

func getCityInfoHandler(c echo.Context) error {
	cityName := c.Param("cityName")
	log.Println(cityName)

	var city City
	err := db.Get(&city, "SELECT * FROM city WHERE Name=?", cityName)
	if errors.Is(err, sql.ErrNoRows) {
		return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such city Name = %s", cityName))
	}
	if err != nil {
		log.Printf("DB Error: %s", err)
		return echo.NewHTTPError(http.StatusInternalServerError, "internal server error")
	}

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

都市が見つかったら200を、見つからなかったら404を返しています。 Postman からリクエストを送ってみましょう。

画像のように返ってきたら成功です。

自分が好きな都市の情報を取得する API を叩いて、そのレスポンスのスクリーンショットを講習会チャンネルに投稿しましょう。

基本問題

都市を追加する API を追加してみましょう。

ヒント 1

都市を追加するということはクライアントから情報を受け取る必要があります。このようなときは、どのメソッドを使えばいいでしょうか。

ヒント 2

メソッドはPOSTを使いましょう。リクエストボディには JSON を使いましょう。どのようにすれば JSON を扱えたでしょうか。

答え
  • main関数内部
go
	e := echo.New()

	e.GET("/cities/:cityName", getCityInfoHandler)
	e.POST("/cities", postCityHandler) //[!code ++]

	e.Start(":8080")
}
  • postCityHandler関数を定義
go
func postCityHandler(c echo.Context) error { //[!code ++]
	var city City        //[!code ++]
	err := c.Bind(&city) //[!code ++]
	if err != nil {      //[!code ++]
		return echo.NewHTTPError(http.StatusBadRequest, "bad request body") //[!code ++]
	} //[!code ++]
	//[!code ++]
	result, err := db.Exec("INSERT INTO city (Name, CountryCode, District, Population) VALUES (?, ?, ?, ?)", city.Name, city.CountryCode, city.District, city.Population) //[!code ++]
	if err != nil {                                                                                                                                                       //[!code ++]
		log.Printf("DB Error: %s", err)                                                   //[!code ++]
		return echo.NewHTTPError(http.StatusInternalServerError, "internal server error") //[!code ++]
	} //[!code ++]
	//[!code ++]
	id, _ := result.LastInsertId() //[!code ++]
	city.ID = int(id)              //[!code ++]
	//[!code ++]
	return c.JSON(http.StatusCreated, city) //[!code ++]
} //[!code ++]

架空の都市や実在する都市を Postman から追加して、レスポンスのスクリーンショットを講習会チャンネルに投稿しましょう。

応用問題

さまざまな API を作ってみましょう。

    • 国の情報を取得する
    • 都市をすべて取得する
    • 既にある都市や国の情報を変更する