📚 Series: Belajar Golang dari Nol sampai Deploy
Install Dependencies
# GORM core
go get gorm.io/gorm
# Driver PostgreSQL
go get gorm.io/driver/postgres
# Untuk load .env file
go get github.com/joho/godotenv
Setup .env
Buat file .env di root project:
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=secret
DB_NAME=todo_db
Jangan lupa tambahkan .env ke .gitignore!
Koneksi Database
Buat file database/db.go:
package database
import (
"fmt"
"log"
"os"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var DB *gorm.DB
func Connect() {
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable TimeZone=Asia/Jakarta",
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_NAME"),
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
if err != nil {
log.Fatal("Gagal konek database:", err)
}
DB = db
log.Println("Database terhubung!")
}
Model dengan GORM
Update models/task.go:
package models
import "gorm.io/gorm"
type Task struct {
gorm.Model // embed: ID, CreatedAt, UpdatedAt, DeletedAt (soft delete!)
Title string `json:"title" gorm:"not null;size:200"`
Completed bool `json:"completed" gorm:"default:false"`
UserID uint `json:"user_id"`
}
// gorm.Model sudah include:
// ID uint
// CreatedAt time.Time
// UpdatedAt time.Time
// DeletedAt gorm.DeletedAt (soft delete)
Auto Migration
// Di main.go
func main() {
godotenv.Load()
database.Connect()
// Auto migrate — buat tabel jika belum ada, update kolom yang berubah
database.DB.AutoMigrate(&models.Task{})
// ... setup router
}
CRUD dengan GORM
Update handlers/task.go:
package handlers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/username/todo-api/database"
"github.com/username/todo-api/models"
)
func GetTasks(c *gin.Context) {
var tasks []models.Task
result := database.DB.Find(&tasks)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal ambil data"})
return
}
c.JSON(http.StatusOK, gin.H{"data": tasks, "total": len(tasks)})
}
func GetTask(c *gin.Context) {
var task models.Task
if err := database.DB.First(&task, c.Param("id")).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
return
}
c.JSON(http.StatusOK, gin.H{"data": task})
}
func CreateTask(c *gin.Context) {
var input struct {
Title string `json:"title" binding:"required,min=1,max=200"`
}
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
task := models.Task{Title: input.Title}
if err := database.DB.Create(&task).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Gagal buat task"})
return
}
c.JSON(http.StatusCreated, gin.H{"data": task})
}
func UpdateTask(c *gin.Context) {
var task models.Task
if err := database.DB.First(&task, c.Param("id")).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
return
}
var input struct {
Title *string `json:"title"`
Completed *bool `json:"completed"`
}
c.ShouldBindJSON(&input)
if input.Title != nil {
task.Title = *input.Title
}
if input.Completed != nil {
task.Completed = *input.Completed
}
database.DB.Save(&task)
c.JSON(http.StatusOK, gin.H{"data": task})
}
func DeleteTask(c *gin.Context) {
var task models.Task
if err := database.DB.First(&task, c.Param("id")).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Task tidak ditemukan"})
return
}
database.DB.Delete(&task) // soft delete!
c.JSON(http.StatusOK, gin.H{"message": "Task berhasil dihapus"})
}
Query Lanjutan GORM
// Filter
database.DB.Where("completed = ?", false).Find(&tasks)
// Order
database.DB.Order("created_at desc").Find(&tasks)
// Limit & Offset (pagination)
page := 1
pageSize := 10
database.DB.Offset((page-1)*pageSize).Limit(pageSize).Find(&tasks)
// Count
var total int64
database.DB.Model(&models.Task{}).Count(&total)
// Search
keyword := "belajar"
database.DB.Where("title ILIKE ?", "%"+keyword+"%").Find(&tasks)
// Transaksi
database.DB.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&task1).Error; err != nil {
return err // auto rollback
}
if err := tx.Create(&task2).Error; err != nil {
return err
}
return nil // commit
})
Komentar 0