はじめに
Udemyの講座「Echo/Go + Reactで始めるモダンWebアプリケーション開発」を基にEcho/Goの部分だけにはなるのですが、勉強した内容をまとめていこうと思います。長くなるので何回かに分けてブログを執筆しようと思っています。
プロジェクトの作成
まずはVSCodeにてフォルダ(GO-REST-API)を作成し,下記コマンドを実行し新しくGoのプロジェクトを作成します。go mod init go-rest-api
ローカルでPostgreSQLを起動
ローカルでpostgresを起動するため、プロジェクト直下にdocker-compose.ymlファイルを作成します。version: "3.8"
services:
dev-postgres:
image: postgres:15.1-alpine
ports:
- 5434:5432
environment:
//PostgreSQLに必要なユーザー名
POSTGRES_USER: udemy
//PostgreSQLに必要なパスワード
POSTGRES_PASSWORD: udemy
//PostgreSQLに必要なDB名
POSTGRES_DB: udemy
restart: always
networks:
- lesson
networks:
lesson:
上記のdocker-compose.ymlを作成したら、docker compose up -d
でコンテナを立ち上げていきます。
※念の為docker ps
でpostgres:15.1-alpineのコンテナが起動しているかを確認しておく。
環境変数の設定
下記のファイルを作成して、アプリケーションに使用する環境変数の設定を行います。PORT=8080
//PostgreSQLに必要なユーザー名
POSTGRES_USER=udemy
//PostgreSQLに必要なパスワード
POSTGRES_PW=udemy
//PostgreSQLに必要なDB名
POSTGRES_DB=udemy
//PostgreSQLのポート番号
POSTGRES_PORT=5434
//PostgreSQLのホスト名
POSTGRES_HOST=localhost
//JWTを生成するためのシークレットキー(任意の値)
SECRET=uu5pveql
//開発ノード
GO_ENV=dev
API_DOMAIN=localhost
//フロントエンドのパス
FE_URL=http://localhost:3000
ユーザーのモデル構造作成
下記のコードは、GORMライブラリを使って、データベースにユーザー情報を保存し、APIレスポンスを生成する際に使用されます。下記のコードは、ユーザーを表すデータ構造 (User) と、APIレスポンスとして使用するデータ構造 (UserResponse) を定義しています。
User 構造体は、ユーザーのID、メールアドレス、パスワード、作成日時、更新日時を格納します。
UserResponse 構造体は、APIレスポンスとして、ユーザーのIDとメールアドレスをクライアントに返します。
package model
import "time"
type User struct {
//構造体のオブジェクトをJSONに変換したときに自動的に小文字に変更
ID uint `json:id gorm:"primaryKey"`
Email string `json:"email" gorm:"unique"`
Password string `json:"password"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type UserResponse struct {
ID uint `json:id gorm:"primaryKey"`
Email string `json:"email" gorm:"unique"`
}
json:
とすると、構造体のオブジェクトをJSONに変換したときに自動的にフィールド名を小文字に変更してくれる。
また、gorm:"primaryKey
はオブジェクトの主キーとして認識する。
gorm:"unique
は重複を許さないユニークなフィールドという制約をつけることが出来る。
6.タスクのモデル構造作成
下記のコードは、Go言語でREST APIアプリケーションで使用されるタスクのデータ構造(モデル)を定義しています。
下記のコードは、タスクを表すデータ構造 (Task) と、APIレスポンスとして使用するデータ構造 (TaskResponse) を定義しています。
Task 構造体は、タスクのID、タイトル、作成日時、更新日時、関連するユーザーの情報を格納します。
TaskResponse 構造体は、APIレスポンスとして、タスクのID、タイトル、作成日時、更新日時をクライアントに返します。
model/task.go
package model
import "time"
type Task struct {
ID uint `json:"id" gorm:"primaryKey"`
Title string `json:"title" gorm:"not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
User User `json: "user" gorm:"foreignKey:UserId: constraint:OnDelete:CASCADE"`
UserId uint `json:"user_id" gorm:"not null"`
}
type TaskResponse struct {
ID uint `json:id gorm: "primaryKey"`
Title string `json: "title" gorm: "not null"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
gorm:"foreignKey:UserId:
はユーザーモデルの主キーであるIDの値がタスクモデルのUserIdに格納されます。
1対多のリレーションを張ることが出来る。(複数のテーブルと関連付けることが出来る)
gorm: "not null"
空の値を許可しない。
constraint:OnDelete:CASCADE
はユーザーを削除した際、削除したユーザーのタスクを自動的に削除します。
DBへの接続
下記のコードは、Go言語でデータベースへの接続を作成し、その接続を管理する関数を提供する db パッケージです。下記のコードは、NewDB() 関数を使って、PostgreSQLデータベースへの接続を作成します。
環境変数 GO_ENV が "dev" に設定されている場合は、.env ファイルから環境変数をロードします。
データベース接続が成功すると、"Connected" を出力して、データベース接続オブジェクトを返します。
CloseDB() 関数は、データベース接続オブジェクトを引数に受け取って、データベース接続を閉じます。
package db
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func NewDB() *gorm.DB {
//環境変数 `GO_ENV` が "dev" に設定されている場合は、`godotenv.Load()` を使って `.env` ファイルから環境変数をロードします。
if os.Getenv("GO_ENV") == "dev" {
err := godotenv.Load()
if err != nil {
log.Fatalln(err)
}
}
//環境変数からPostgreSQLの接続URLを作成します。
url := fmt.Sprintf("postgres://%s:%s@%s:%s/%s", os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PW"), os.Getenv("POSTGRES_HOST"),
os.Getenv("POSTGRES_PORT"), os.Getenv("POSTGRES_DB"))
//PostgreSQLドライバーを使って、データベースに接続します。
db, err := gorm.Open(postgres.Open(url), &gorm.Config{})
//エラーが発生した場合、ログにエラーメッセージを出力してプログラムを終了します。
if err != nil {
log.Fatalln(err)
}
//接続が成功した場合、"Connected" を出力します。
fmt.Println("Connected")
//データベース接続オブジェクトを返します。
return db
}
func CloseDB(db *gorm.DB) {
//GORMデータベース接続から、SQLデータベース接続オブジェクトを取得します。
sqlDB, _ := db.DB()
//SQLデータベース接続を閉じます。エラーが発生した場合、ログにエラーメッセージを出力してプログラムを終了します。
if err := sqlDB.Close(); err != nil {
log.Fatalln(err)
}
}
※外部パッケージを使用するのでgo mod tidy
コマンドを実行してインストールします。
マイグレーション
下記のコードは、REST APIアプリケーションの初期設定として、データベースへの接続を作成し、データベーススキーマを自動的にマイグレートします。ここでのポイントは、AutoMigrate 関数を使い、User 構造体と Task 構造体のスキーマをデータベースに自動的に作成します。
package main
import (
"fmt"
"go-rest-api/db"
"go-rest-api/model"
)
func main() {
//`db` パッケージの `NewDB()` 関数を呼び出して、データベースへの接続を作成し、接続オブジェクト `dbConn` に格納します。
dbConn := db.NewDB()
//`defer` キーワードを使って、`main` 関数の最後(プログラムの終了前)に "Successfully Migrated" を出力する処理を登録します。
defer fmt.Println("Successfully Migrated")
//`defer` キーワードを使って、`main` 関数の最後(プログラムの終了前)に `db` パッケージの `CloseDB()` 関数を呼び出して、データベース接続を閉じる処理を登録します。
defer db.CloseDB(dbConn)
//`dbConn.AutoMigrate(&model.User{}, &model.Task{})`: `dbConn` オブジェクトを使って、`User` 構造体と `Task` 構造体のスキーマをデータベースに自動的にマイグレートします。
dbConn.AutoMigrate(&model.User{}, &model.Task{})
}
下記コマンドでマイグレーションを実行します。
GO_ENV=dev go run migrate/migrate.go
成功すると下記のメッセージが出てきます。
Connected
Successfully Migrated
データベースにテーブルが生成されているか確認
pgAdminにて確認していきます。 初めて起動する場合は、好きなパスワードを設定してください。 下記画像のAdd New Serverをクリックします。下記画像のNameを設定を行います。(好きな名前で大丈夫です)
Connectionタブに移動して.envで設定している環境変数を入力。
tasksテーブルにtask.goで定義したフィールドが反映されているか確認。
tasksテーブルにtask.goで定義したフィールドが反映されています。
今度はusersテーブルにuser.goで定義したフィールドが反映されているか確認。
usersテーブルにuser.goで定義したフィールドが反映されています。
まとめ
今回はマイグレーションのやり方についてまとめました。次回はユーザーの新規追加、ログイン、ログアウト機能を実装していきます。