Zenn
Closed5

golang + postgresqlでCRUD

not75743not75743
$ tree
.
├── .env
├── go.mod
├── go.sum
├── internal
│   ├── db
│   │   └── db.go
│   ├── handlers
│   │   └── user.go
│   └── models
│       └── user.go
├── main.go
└── Makefile
not75743not75743

main.go

エントリーポイント

main.go
package main

import (
	"fmt"
	"github.com/joho/godotenv"
	"log"
	"net/http"
	"os"

	"golang-crud/internal/db"
	"golang-crud/internal/handlers"
)

func main() {
	// .envファイルから環境変数をロード
	if err := godotenv.Load(); err != nil {
		log.Println("No .env file found. 環境変数はシステムから取得されます")
	}

	// 環境変数からDB接続情報を取得
	host := os.Getenv("DB_HOST")
	user := os.Getenv("DB_USER")
	password := os.Getenv("DB_PASSWORD")
	dbname := os.Getenv("DB_NAME")
	port := os.Getenv("DB_PORT")
	sslmode := os.Getenv("DB_SSLMODE")

	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s", host, user, password, dbname, port, sslmode)

	// DBの初期化
	db.InitDB(dsn)

	// ルーティングの設定
	http.HandleFunc("/create", handlers.CreateUser)
	http.HandleFunc("/list", handlers.ListUsers)
	http.HandleFunc("/user", handlers.GetUser)
	http.HandleFunc("/update", handlers.UpdateUser)
	http.HandleFunc("/delete", handlers.DeleteUser)

	fmt.Println("サーバー起動: http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}
not75743not75743

db.go

db接続

internal/db/db.go
package db

import (
	"log"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"golang-crud/internal/models"
)

var DB *gorm.DB

func InitDB(dsn string) {
	var err error
	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatalf("データベース接続に失敗しました: %v", err)
	}

	if err = DB.AutoMigrate(&models.User{}); err != nil {
		log.Fatalf("自動マイグレーションに失敗しました: %v", err)
	}
}

not75743not75743

model

モデル

internal/models/user.go
package models

type User struct {
	ID   uint   `gorm:"primaryKey" json:"id"`
	Name string `json:"name"`
}
not75743not75743

handler

各種CRUDのハンドラ

internal/handlers/user.go
package handlers

import (
	"encoding/json"
	"golang-crud/internal/db"
	"golang-crud/internal/models"
	"net/http"
	"strconv"
)

func CreateUser(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	var user models.User
	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if result := db.DB.Create(&user); result.Error != nil {
		http.Error(w, result.Error.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(user)
}

func ListUsers(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	var users []models.User
	if result := db.DB.Find(&users); result.Error != nil {
		http.Error(w, result.Error.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(users)
}

func GetUser(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	idStr := r.URL.Query().Get("id")
	if idStr == "" {
		http.Error(w, "Missing id parameter", http.StatusBadRequest)
		return
	}

	id, err := strconv.Atoi(idStr)
	if err != nil {
		http.Error(w, "Invalid id parameter", http.StatusBadRequest)
		return
	}

	var user models.User
	if result := db.DB.First(&user, id); result.Error != nil {
		http.Error(w, result.Error.Error(), http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(user)
}

func UpdateUser(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPut {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	var user models.User
	if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if user.ID == 0 {
		http.Error(w, "Missing user ID", http.StatusBadRequest)
		return
	}

	result := db.DB.Model(&models.User{}).Where("id = ?", user.ID).Updates(user)
	if result.Error != nil {
		http.Error(w, result.Error.Error(), http.StatusInternalServerError)
		return
	}
	if result.RowsAffected == 0 {
		http.Error(w, "User not found", http.StatusNotFound)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(user)
}

func DeleteUser(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodDelete {
		http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
		return
	}

	idStr := r.URL.Query().Get("id")
	if idStr == "" {
		http.Error(w, "Missing id parameter", http.StatusBadRequest)
		return
	}

	id, err := strconv.Atoi(idStr)
	if err != nil {
		http.Error(w, "Invalid id parameter", http.StatusBadRequest)
		return
	}

	result := db.DB.Delete(&models.User{}, id)
	if result.Error != nil {
		http.Error(w, result.Error.Error(), http.StatusInternalServerError)
		return
	}
	if result.RowsAffected == 0 {
		http.Error(w, "User not found", http.StatusNotFound)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

このスクラップは10日前にクローズされました
ログインするとコメントできます