WEBその他

【Go言語】初学者向けCRUD操作してみた。

WEB
この記事は約12分で読めます。

はじめ

案件にてGo言語を触ることになったので、かなり初歩的な内容ですがCRUD操作をしてみました。

Go言語の概要や活用方法を知りたい方はこちらをご覧ください。

ディレクトリ構成とバージョン

ディレクトリ構成

最終的なディレクトリ構成は以下になります。

.
├── README.md
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
└── todo-app

バージョン

go 1.22.0
echo 4.11.4
gorm 1.25.7

DBの環境を作成する

まず初めに、DBの環境を作成します。今回はdockerでpostgres環境を立てていきたいと思います。

version: "3.8"
services:
  db:
    container_name: db_todo
    image: postgres:latest
    tty: true
    platform: linux/x86_64
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: go_todo  # データベース名
      POSTGRES_USER: test  # データベースユーザー名
      POSTGRES_PASSWORD: password 
docker compose up --build

上記コマンドでdockerを立ち上げることで、postgresの環境が立ち上がります。

Go言語ファイルの作成 ~Hello World!~

go.modファイルの作成

go.modとは、モジュールの依存関係を管理するためのファイルで、プロジェクトのルートディレクトリ

に配置されます。

プロジェクトが依存する外部モジュールのリストとそれぞれのバージョンが記載されており、プロジェク

トのビルドが再現可能になります。

go mod init todo-app

上記コマンドを行い、go.modファイルを作成します。

Hello World!

main.goファイルを作成し、中身を下記にしてください。

package main

import (
	"net/http"
	"github.com/labstack/echo/v4"
)

func  main()  {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":8989"))
}

今回、Echoフレームワークを使用していきたいと思いますので、importしていきます。

go mod tidy

この状態で、上記コマンドを実行すると、プロジェクトの全ソースコードを解析し、現在のコードベース

で実際に必要な依存関係を正確に把握します。その結果、go.modgo.sum ファイルが更新され、依存関

係が整理されます。

go build

Go言語は、コンパイル型の言語のため上記コマンドで実行ファイルを作成します。

go run todo-app

上記コマンドで実行ファイルを実行し、http://localhost:8989/をブラウザで開くと「Hello, World!」

が表示されていると思います。

DBとの接続 〜gorm〜

main.goを下記に書き換えます。

package main

import (
	"net/http"
	"log"
	"time"
	"github.com/labstack/echo/v4"
	"gorm.io/gorm"
	"gorm.io/driver/postgres"
	"gorm.io/gorm/logger"
)

type Todo struct {
	gorm.Model
	Content  	string `gorm:"not null"`
	Done 	 	bool
	Until 	 	time.Time
}

func main() {
	connectionString := "host=localhost user=test password=password dbname=go_todo port=5432 sslmode=disable TimeZone=Asia/Tokyo"
	db, err := gorm.Open(postgres.Open(connectionString), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		log.Fatal("Failed to connect to database:", err)
	}

	err = db.AutoMigrate(&Todo{})
	if err != nil {
		log.Fatal("Failed to migrate database:", err)
	}

	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":8989"))
}

まず必要とされる外部パッケージ等をインポートします。

structから始まる箇所では、C言語と同様に構造体の型を定義していきます。

その後データベースの接続と初期化、テーブルの自動マイグレーションを行なっております。

こちらのファイルを実行することで、最初に作成したpostgresのデータベースにTodoテーブルが作成さ

れていると思います。

ルート作成

POST

まずは、POSTを作成し、データを作成できるようにしていきます。下記のコードをmian.goに追記して

いきます。

	e.POST("/todos", func(c echo.Context) error {
        var todo Todo
        if err := c.Bind(&todo); err != nil {
            return err
        }
        if result := db.Create(&todo); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusCreated, todo)
    })

こちらのコードを追加した後、再度build,runを行います。

その後、curlコマンドや、Postmanにてhttp://localhost:8989/todosこちらを叩いていきます。

Postmanにてbodyに加えるファイルをデータは下記に記載しておきます。

{
  "Content": "新しいTodoアイテム",
  "Done": false,
  "Until": "2024-12-31T23:59:59Z"
}

GET

次に、下記ファイルを追記し、http://localhost:8989/todos/でGETを叩くと全データが取得でき、

http://localhost:8989/todos/1で選択したデータを取得できていると思います。

    e.GET("/todos", func(c echo.Context) error {
        var todos []Todo
        if result := db.Find(&todos); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todos)
    })

    e.GET("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        var todo Todo
        if result := db.First(&todo, id); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todo)
    })

PUT

こちらも下記コードを追加した後、bodyに修正したい内容を記載しhttp://localhost:8989/todos/1を

叩くと修正できるかと思います。

    e.PUT("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        var todo Todo
        if err := c.Bind(&todo); err != nil {
            return err
        }
        todo.ID = 0 // GORMが新しいレコードとして扱わないようにIDをリセット
        if result := db.Model(&Todo{}).Where("id = ?", id).Updates(todo); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todo)
    })

DELETE

最後に削除のコードを追記していきます。

    e.DELETE("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        if result := db.Delete(&Todo{}, id); result.Error != nil {
            return result.Error
        }
        return c.NoContent(http.StatusNoContent)
    })

最終のコード

package main

import (
	"net/http"
	"log"
	"time"
	"github.com/labstack/echo/v4"
	"gorm.io/gorm"
	"gorm.io/driver/postgres"
	"gorm.io/gorm/logger"
)

type Todo struct {
	gorm.Model
	
	Content  	string `gorm:"not null"`
	Done 	 	bool
	Until 	 	time.Time
}

func main() {
	connectionString := "host=localhost user=test password=password dbname=go_todo port=5432 sslmode=disable TimeZone=Asia/Tokyo"
	db, err := gorm.Open(postgres.Open(connectionString), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		log.Fatal("Failed to connect to database:", err)
	}

	err = db.AutoMigrate(&Todo{})
	if err != nil {
		log.Fatal("Failed to migrate database:", err)
	}
	
	e := echo.New()

	e.POST("/todos", func(c echo.Context) error {
        var todo Todo
        if err := c.Bind(&todo); err != nil {
            return err
        }
        if result := db.Create(&todo); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusCreated, todo)
    })

	e.GET("/todos", func(c echo.Context) error {
        var todos []Todo
        if result := db.Find(&todos); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todos)
    })

    e.GET("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        var todo Todo
        if result := db.First(&todo, id); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todo)
    })

	e.PUT("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        var todo Todo
        if err := c.Bind(&todo); err != nil {
            return err
        }
        todo.ID = 0
        if result := db.Model(&Todo{}).Where("id = ?", id).Updates(todo); result.Error != nil {
            return result.Error
        }
        return c.JSON(http.StatusOK, todo)
    })

    e.DELETE("/todos/:id", func(c echo.Context) error {
        id := c.Param("id")
        if result := db.Delete(&Todo{}, id); result.Error != nil {
            return result.Error
        }
        return c.NoContent(http.StatusNoContent)
    })
	
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":8989"))
}

おわり

以上簡単な内容ですがとりあえずGo言語を触りたい方は試してみてはいかがでしょうか。

参考

Go言語プログラミングエッセンス