GMashtalyar

docker-compose.yml

.

version: '3'
services:
  db:
    image: postgres:13
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: testdb
    ports:
      - "5432:5432"
    volumes:
      - dbdata:/var/lib/postgresql/data

  app:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - db
    environment:
      DB_HOST: db
      DB_USER: postgres
      DB_PASSWORD: password
      DB_NAME: testdb
    volumes:
      - .:/app
    command: ["go", "run", "main.go"]

volumes:
  dbdata:

Dockerfile

.

FROM golang:1.22-alpine


WORKDIR /app

# Copy the go.mod and go.sum files
COPY go.mod go.sum ./

# Download the Go modules
RUN go mod download


COPY . .


EXPOSE 8080


CMD ["go", "run", "main.go"]

Models

first letter must be Uppercase

package models

import (
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Name  string `json:"name"` // first letter must be Uppercase
	Email string `json:"email"`
}

main.go

.

package main

import (
	"Gin_Gorm_Postgres/models"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

var DB *gorm.DB

func initDB() {
	var err error

	dbHost := os.Getenv("DB_HOST")
	dbUser := os.Getenv("DB_USER")
	dbPassword := os.Getenv("DB_PASSWORD")
	dbName := os.Getenv("DB_NAME")

	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=5432 sslmode=disable", dbHost, dbUser, dbPassword, dbName)
	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal("Failed to connect to database: ", err)
	}
	DB.AutoMigrate(&models.User{})
}

func main() {
	router := gin.Default()
	initDB()
	router.LoadHTMLGlob("templates/*")

	router.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})

	router.GET("/", func(c *gin.Context) {
		var users []models.User
		DB.Find(&users)
		c.HTML(http.StatusOK, "index.html", gin.H{"users": users})
	})

	router.GET("/users/new", func(c *gin.Context) {
		c.HTML(http.StatusOK, "create.html", nil)
	})

	router.POST("/users", func(c *gin.Context) {
		var user models.User
		if err := c.ShouldBind(&user); err != nil { // ShouldBindJSON
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		if result := DB.Create(&user); result.Error != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
			return
		}
		c.JSON(http.StatusOK, user)
		// c.Redirect(http.StatusMovedPermanently, "/")

	})

	router.GET("/users/edit/:id", func(c *gin.Context) {
		id := c.Param("id")
		var user models.User
		if err := DB.First(&user, id).Error; err != nil {
			c.String(http.StatusNotFound, "User not found")
			return
		}
		c.HTML(http.StatusOK, "edit.html", gin.H{"user": user})
	})

	router.POST("/users/update/:id", func(c *gin.Context) {
		id := c.Param("id")
		var user models.User
		if err := DB.First(&user, id).Error; err != nil {
			c.String(http.StatusNotFound, "User not found")
			return
		}
		if err := c.ShouldBind(&user); err != nil {
			c.HTML(http.StatusBadRequest, "edit.html", gin.H{"error": err.Error(), "user": user})
			return
		}

		DB.Save(&user)
		c.Redirect(http.StatusMovedPermanently, "/")
	})

	router.GET("/users/delete/:id", func(c *gin.Context) {
		id := c.Param("id")
		var user models.User
		if err := DB.Delete(&user, id).Error; err != nil {
			c.String(http.StatusNotFound, "User not found")
			return
		}
		c.Redirect(http.StatusMovedPermanently, "/")
	})

	router.Run(":8080")
}

Simple templates

Uppercase...

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Create User</title>
</head>
<body>
    <h1>Create New User</h1>
    <form action="/users" method="POST">
        <label for="name">Name:</label>
        <input type="text" id="name" name="Name"> <!-- Uppercase name -->
        <label for="email">Email:</label>
        <input type="email" id="email" name="Email"> <!-- Uppercase name -->
        <button type="submit">Create</button>
    </form>
    <a href="/">Back to List</a>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Users</title>
</head>
<body>
    <h1>Users List</h1>
    <a href="/users/new">Create New User</a>
    <ul>
        {{range .users}}
        <li>
            {{.Name}} ({{.Email}})
            <a href="/users/edit/{{.ID}}">Edit</a>
            <a href="/users/delete/{{.ID}}">Delete</a>
        </li>
        {{else}}
        <li>No users found</li>
        {{end}}
    </ul>
</body>
</html>