
Fitur authentication (auth) sangat penting dalam sebuah website, terutama jika platform tersebut melibatkan data pengguna. Dengan auth, pengguna dapat melakukan registrasi dan login ke akun mereka untuk mengelola informasi penting, seperti profil, transaksi, atau pengaturan lainnya.
Tanpa sistem auth yang baik, data pengguna bisa rentan terhadap akses tidak sah, yang berisiko menyebabkan kebocoran informasi atau penyalahgunaan akun.
Sistem auth biasanya mencakup beberapa komponen utama, seperti login, registrasi, otorisasi berdasarkan peran pengguna, serta perlindungan terhadap serangan seperti brute force dan session hijacking. Oleh karena itu, implementasi auth yang aman sangat diperlukan dalam pengembangan website profesional.
Belajar Golang, Gin, dan JWT untuk Fitur Auth pada Website Toko Sewa Mobil
Dalam artikel ini, kita akan membahas bagaimana membangun fitur auth menggunakan Golang, Gin, dan JWT untuk sebuah website toko sewa mobil.
Golang adalah bahasa yang cepat dan efisien, sementaara Gin adalah framework yang mempermudah pengembangan web API di Golang. Sedangkan JWT (JSON Web Token) digunakan sebagai mekanisme untuk mengelola sesi login pengguna dengan aman.
Dengan kombinasi teknologi ini, kita bisa membuat sistem auth yang memungkinkan pengguna untuk mendaftar dan masuk ke akun meareka, sehingga mereka dapat menyewa mobil, melihat riwayat pemesanan, atau mengelola informasi lainnya dengan aman. Fitur ini juga membantu memastikan bahwa hanya pengguna yang sudah terautentikasi yang dapaat mengakses data sensitif.
Di artikel selanjutnya, kita akan membahas bagaimana mengimplementasiakan fitur auth ini dengan kode secara bertahap.
Membuat Proyek Golang Terbaru dan Menginstal Dependensi
Untuk memulai proyek Golang terbaru, pastikan Golang telah diinstal di sistem. Gunakan versi terbaru agar mendapatkan fitur terbaru dan perbaikan keamanan.
Buka terminal dan buat folder proyek:
mkdir rental-car-api
cd rental-car-api
go mod init github.com/yourusername/rental-car-api
Perintah ini menginisialisasi proyek dengan go.mod
, yang mengelola dependensi proyek.
Setelah itu, instal beberapa library yang akan digunakan:
go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
go get -u github.com/joho/godotenv
go get -u github.com/golang-jwt/jwt/v5
go get -u github.com/google/uuid
Gin digunakan untuk membangun web API, GORM untuk ORM, dotenv untuk membaca konfigurasi dari file .env
, JWT untuk autentikasi, dan UUID untuk membuat ID unik.
Buat file .env
di root proyek untuk menyimpan konfigurasi database:
DB_USER=root
DB_PASSWORD=password
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=rental_car
JWT_SECRET=your_secret_key
File ini digunakan untuk menyimpan kredensial sensitif agar tidak hardcoded dalam kode sumber.
Konfigurasi Database dan Auto Migrate dengan GORM
Buat folder database
dan buat file database/connection.go
:
package database
import (
"fmt"
"log"
"os"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"github.com/joho/godotenv"
"github.com/yourusername/rental-car-api/models"
)
var DB *gorm.DB
func Connect() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_NAME"),
)
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
DB.AutoMigrate(&models.User{}, &models.Car{})
}
Kode ini membaca konfigurasi database dari .env
, membuat koneksi ke MySQL, dan melakukan auto migrate untuk tabel users
dan cars
.
Membuat Model User
Buat folder models
, lalu buat file models/user.go
:
package models
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:char(36);primary_key" json:"id"`
Name string `gorm:"not null" json:"name"`
Email string `gorm:"unique;not null" json:"email"`
Password string `gorm:"not null" json:"-"`
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.ID = uuid.New()
return
}
Model ini menggunakan UUID sebagai primary key agar lebih aman dan unik dibandingkan dengan integer. Fungsi BeforeCreate
memastikan bahwa setiap pengguna memiliki UUID baru sebelum disimpan ke database.
Membuat Model Car
Buat file models/car.go
:
package models
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
type Car struct {
ID uuid.UUID `gorm:"type:char(36);primary_key" json:"id"`
Brand string `gorm:"not null" json:"brand"`
Model string `gorm:"not null" json:"model"`
Year int `gorm:"not null" json:"year"`
Price float64 `gorm:"not null" json:"price"`
Status string `gorm:"default:'available'" json:"status"`
}
func (c *Car) BeforeCreate(tx *gorm.DB) (err error) {
c.ID = uuid.New()
return
}
Sama seperti model User
, model Car
menggunakan UUID untuk ID. Status mobil secara default akan diatur ke "available"
.
Menghubungkan Database ke main.go
Buka main.go
dan tambahkan kode berikut:
package main
import (
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/database"
)
func main() {
database.Connect()
r := gin.Default()
r.Run(":8080")
}
Kode ini memastikan bahwa koneksi ke database sudah terjalin sebelum server dijalankan pada port 8080
.
Menjalankan Proyek
Jalankan proyek dengan perintah berikut:
go run main.go
Jika berhasil, database akan terkoneksi dan tabel users
serta cars
akan dibuat secara otomatis. Untuk mengecek apakah tabel berhasil dibuat, jalankan perintah berikut di MySQL:
SHOW TABLES;
DESCRIBE users;
DESCRIBE cars;
Dengan langkah ini, sistem database sudah siap untuk digunakan dalam proyek website toko sewa mobil.
Struktur Folder untuk Proyek Website Sewa Mobil Online (Golang + Gin + GORM)
Agar proyek website sewa mobil online lebih terstruktur dan mudah dikelola, folder dan file harus diorganisir dengan baik. Berikut adalah struktur yang direkomendasikan beserta penjelasan detailnya:
rental-car-api/
│── .env
│── .gitignore
│── go.mod
│── go.sum
│── main.go
│── database/
│ ├── connection.go
│── models/
│ ├── user.go
│ ├── car.go
│── controllers/
│ ├── authController.go
│ ├── carController.go
│── routes/
│ ├── authRoutes.go
│ ├── carRoutes.go
│── middlewares/
│ ├── authMiddleware.go
│── utils/
│ ├── jwtHelper.go
│ ├── hashHelper.go
│── config/
│ ├── config.go
│── storage/
│ ├── uploads/
Penjelasan Detail Struktur Folder dan Fungsinya
1. Root Folder (rental-car-api/
)
Folder utama proyek yang berisi file konfigurasi utama dan file entry point (main.go
).
File di dalam root folder:
.env
→ File untuk menyimpan variabel lingkungan (environment variables) seperti konfigurasi database, secret key JWT, dan lainnya..gitignore
→ Menentukan file atau folder yang tidak perlu di-track oleh Git, sepertinode_modules
,uploads/
, atau file cache.go.mod
→ File yang berisi daftar dependensi proyek Golang yang dibuat dengango mod init
.go.sum
→ Menyimpan versi spesifik dari setiap dependensi yang diunduh.main.go
→ Entry point dari aplikasi yang menjalankan server Gin dan menghubungkan berbagai komponen proyek.
2. Folder database/
(Koneksi dan Migrasi Database)
Folder ini berisi file untuk mengatur koneksi database dan melakukan migrasi otomatis.
File di dalam folder database/
connection.go
→ Berisi fungsi untuk menghubungkan Golang dengan database (MySQL, PostgreSQL, atau SQLite) menggunakan GORM.
Contoh kode di database/connection.go
:
package database
import (
"fmt"
"log"
"os"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"github.com/joho/godotenv"
"github.com/yourusername/rental-car-api/models"
)
var DB *gorm.DB
func Connect() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_NAME"),
)
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
DB.AutoMigrate(&models.User{}, &models.Car{})
}
3. Folder models/
(Definisi Struktur Data atau Tabel Database)
Folder ini berisi definisi model yang merepresentasikan tabel dalam database.
File di dalam folder models/
user.go
→ Model pengguna (users
).car.go
→ Model mobil (cars
).
Contoh kode di models/user.go
:
package models
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:char(36);primary_key" json:"id"`
Name string `gorm:"not null" json:"name"`
Email string `gorm:"unique;not null" json:"email"`
Password string `gorm:"not null" json:"-"`
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.ID = uuid.New()
return
}
4. Folder controllers/
(Logika Bisnis dan Fungsi API)
Folder ini berisi handler yang menangani permintaan API dan mengembalikan respons ke client.
File di dalam folder controllers/
authController.go
→ Mengelola registrasi, login, dan pengelolaan sesi.carController.go
→ Mengelola data mobil, seperti menampilkan daftar mobil atau mengubah status mobil.
Contoh kode di controllers/authController.go
:
package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/database"
"github.com/yourusername/rental-car-api/models"
)
func GetUsers(c *gin.Context) {
var users []models.User
database.DB.Find(&users)
c.JSON(http.StatusOK, users)
}
5. Folder routes/
(Pengaturan Rute API)
Folder ini berisi daftar endpoint yang digunakan dalam aplikasi.
File di dalam folder routes/
authRoutes.go
→ Rute untuk login, registrasi, dan autentikasi.carRoutes.go
→ Rute untuk pengelolaan mobil.
Contoh kode di routes/authRoutes.go
:
package routes
import (
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/controllers"
)
func AuthRoutes(r *gin.Engine) {
auth := r.Group("/auth")
{
auth.GET("/users", controllers.GetUsers)
}
}
6. Folder middlewares/
(Middleware untuk Proteksi API)
Folder ini berisi middleware yang digunakan untuk validasi dan keamanan API.
File di dalam folder middlewares/
authMiddleware.go
→ Middleware untuk memvalidasi JWT token.
Contoh kode di middlewares/authMiddleware.go
:
package middlewares
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" || !strings.HasPrefix(token, "Bearer ") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
c.Next()
}
}
7. Folder utils/
(Helper Function atau Utility)
Folder ini berisi fungsi pembantu yang sering digunakan dalam proyek.
File di dalam folder utils/
jwtHelper.go
→ Fungsi untuk membuat dan memverifikasi token JWT.hashHelper.go
→ Fungsi untuk melakukan hash dan verifikasi password.
8. Folder config/
(Konfigurasi Tambahan)
Folder ini digunakan untuk menyimpan konfigurasi tambahan seperti pengaturan aplikasi.
File di dalam folder config/
config.go
→ Menyediakan konfigurasi global untuk proyek.
9. Folder storage/
(Penyimpanan File Uploads)
Folder ini digunakan untuk menyimpan file gambar atau dokumen yang diunggah oleh pengguna.
Folder di dalam storage/
uploads/
→ Folder tempat penyimpanan file yang diunggah.
Dengan struktur folder ini, proyek website sewa mobil online akan lebih terorganisir dan mudah dipahami, terutama jika dikembangkan dalam tim.
Membuat CRUD Data Users pada Controller, Routes, dan JWT Utils
CRUD (Create, Read, Update, Delete) adalah dasar dalam pengelolaan data pengguna. Implementasi ini mencakup registrasi (sign up), login (sign in), membaca daftar pengguna, memperbarui data pengguna, dan menghapus pengguna.
Membuat Controller untuk CRUD Users
Buat file controllers/userController.go
yang menangani operasi CRUD untuk tabel users
.
package controllers
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/database"
"github.com/yourusername/rental-car-api/models"
"github.com/yourusername/rental-car-api/utils"
"github.com/yourusername/rental-car-api/middlewares"
)
func RegisterUser(c *gin.Context) {
var user models.User
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
hashedPassword, err := utils.HashPassword(user.Password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to hash password"})
return
}
user.Password = hashedPassword
if err := database.DB.Create(&user).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create user"})
return
}
c.JSON(http.StatusCreated, gin.H{"message": "User registered successfully"})
}
func LoginUser(c *gin.Context) {
var input models.User
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var user models.User
if err := database.DB.Where("email = ?", input.Email).First(&user).Error; err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
return
}
if !utils.CheckPasswordHash(input.Password, user.Password) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
return
}
token, err := utils.GenerateJWT(user.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
}
func GetUsers(c *gin.Context) {
var users []models.User
database.DB.Find(&users)
c.JSON(http.StatusOK, users)
}
func UpdateUser(c *gin.Context) {
userID := c.Param("id")
var user models.User
if err := database.DB.First(&user, userID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
if err := c.ShouldBindJSON(&user); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
database.DB.Save(&user)
c.JSON(http.StatusOK, gin.H{"message": "User updated successfully", "user": user})
}
func DeleteUser(c *gin.Context) {
userID := c.Param("id")
if err := database.DB.Delete(&models.User{}, userID).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete user"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"})
}
Membuat Routing untuk User CRUD
Buat file routes/userRoutes.go
yang akan mengatur endpoint untuk fitur CRUD pengguna.
package routes
import (
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/controllers"
"github.com/yourusername/rental-car-api/middlewares"
)
func UserRoutes(r *gin.Engine) {
users := r.Group("/users")
{
users.POST("/register", controllers.RegisterUser)
users.POST("/login", controllers.LoginUser)
users.GET("/", middlewares.AuthMiddleware(), controllers.GetUsers)
users.PUT("/:id", middlewares.AuthMiddleware(), controllers.UpdateUser)
users.DELETE("/:id", middlewares.AuthMiddleware(), controllers.DeleteUser)
}
}
Routing ini memastikan bahwa mendapatkan daftar pengguna, mengupdate pengguna, dan menghapus pengguna hanya bisa dilakukan oleh pengguna yang telah login (menggunakan middleware AuthMiddleware
).
Membuat JWT Utils untuk Autentikasi
Buat file utils/jwtHelper.go
yang berisi fungsi untuk membuat dan memverifikasi token JWT.
package utils
import (
"time"
"os"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
var jwtSecret = []byte(os.Getenv("JWT_SECRET"))
func GenerateJWT(userID uuid.UUID) (string, error) {
claims := jwt.MapClaims{
"user_id": userID.String(),
"exp": time.Now().Add(time.Hour * 24).Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
func VerifyJWT(tokenString string) (*jwt.Token, error) {
return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
}
Membuat Utils untuk Hashing Password pada Proses Sign Up dan Sign In
Agar password aman, sebaiknya disimpan dalam bentuk hashed password menggunakan bcrypt.
Buat file utils/hashHelper.go
untuk menangani hashing password.
package utils
import (
"golang.org/x/crypto/bcrypt"
)
// HashPassword melakukan hashing pada password sebelum disimpan ke database
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
// CheckPasswordHash memeriksa apakah password yang diberikan cocok dengan hash yang tersimpan di database
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
Ketika pengguna melakukan registrasi, password akan di-hash sebelum disimpan ke database menggunakan fungsi HashPassword
.
Saat pengguna login, password yang dimasukkan akan dibandingkan dengan yang ada di database menggunakan CheckPasswordHash
.
Menghubungkan Semua ke main.go
Terakhir, pastikan semua komponen sudah terhubung di main.go
untuk menjalankan server dengan fitur CRUD Users.
package main
import (
"github.com/gin-gonic/gin"
"github.com/yourusername/rental-car-api/database"
"github.com/yourusername/rental-car-api/routes"
"github.com/joho/godotenv"
"log"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
database.Connect()
r := gin.Default()
routes.UserRoutes(r)
r.Run(":8080")
}
Server akan berjalan di port 8080, dan API CRUD Users bisa digunakan.
Menjalankan Proyek dan Menguji API
Jalankan server dengan perintah:
go run main.go
Gunakan Postman atau cURL untuk menguji API:
- Registrasi User →
POST <http://localhost:8080/users/register
> - Login User →
POST <http://localhost:8080/users/login
> - Get All Users (Protected) →
GET <http://localhost:8080/users/
> - Update User (Protected) →
PUT <http://localhost:8080/users/:id
> - Delete User (Protected) →
DELETE <http://localhost:8080/users/:id
>
Dengan implementasi ini, CRUD pengguna dalam proyek website sewa mobil online telah berhasil dibuat dengan Golang, Gin, GORM, dan JWT.
Tata Cara Uji Coba Endpoint APIs Menggunakan Postman
Untuk memastikan API bekerja dengan benar, Postman dapat digunakan untuk melakukan uji coba. Berikut adalah cara melakukan pengujian pada endpoint API yang telah dibuat dengan Golang, Gin, GORM, dan JWT.
Buka Postman dan buat request baru untuk setiap endpoint yang akan diuji.
1. Uji Coba Registrasi User (Sign Up)
Pilih metode POST, lalu masukkan URL:
<http://localhost:8080/users/register>
Pada tab Body, pilih raw dan JSON, lalu masukkan data berikut:
{
"name": "John Doe",
"email": "[email protected]",
"password": "password123"
}
Klik Send. Jika berhasil, respons yang diterima:
{
"message": "User registered successfully"
}
2. Uji Coba Login User (Sign In) dan Mendapatkan JWT Token
Pilih metode POST, lalu masukkan URL:
<http://localhost:8080/users/login>
Pada tab Body, pilih raw dan JSON, lalu masukkan data berikut:
{
"email": "[email protected]",
"password": "password123"
}
Klik Send. Jika berhasil, respons yang diterima akan berisi token JWT seperti ini:
{
"token": "eyJhbGciOiJIUzI1NiIsIn..."
}
Salin token yang diberikan karena akan digunakan untuk mengakses endpoint yang memerlukan autentikasi.
3. Uji Coba Mendapatkan Daftar Pengguna (Protected Endpoint)
Pilih metode GET, lalu masukkan URL:
<http://localhost:8080/users/>
Buka tab Headers dan tambahkan Authorization dengan nilai:
Bearer <JWT_TOKEN_YANG_TELAH_DIPEROLEH>
Klik Send. Jika token valid, respons yang diterima akan berupa daftar pengguna:
[
{
"id": "3f36e750-4535-4a9f-9049-89a19f08d0f2",
"name": "John Doe",
"email": "[email protected]"
}
]
Jika token tidak valid atau tidak disertakan, respons yang diterima adalah:
{
"error": "Unauthorized"
}
4. Uji Coba Update Data User (Protected Endpoint)
Pilih metode PUT, lalu masukkan URL dengan ID user yang ingin diperbarui:
<http://localhost:8080/users/3f36e750-4535-4a9f-9049-89a19f08d0f2>
Buka tab Headers dan tambahkan Authorization seperti sebelumnya.
Pada tab Body, pilih raw dan JSON, lalu masukkan data berikut:
{
"name": "John Updated",
"email": "[email protected]"
}
Klik Send. Jika berhasil, respons yang diterima:
{
"message": "User updated successfully",
"user": {
"id": "3f36e750-4535-4a9f-9049-89a19f08d0f2",
"name": "John Updated",
"email": "[email protected]"
}
}
5. Uji Coba Hapus User (Protected Endpoint)
Pilih metode DELETE, lalu masukkan URL dengan ID user yang ingin dihapus:
<http://localhost:8080/users/3f36e750-4535-4a9f-9049-89a19f08d0f2>
Buka tab Headers dan tambahkan Authorization seperti sebelumnya.
Klik Send. Jika berhasil, respons yang diterima:
{
"message": "User deleted successfully"
}
Jika user dengan ID tersebut tidak ditemukan, respons yang diterima adalah:
{
"error": "User not found"
}
Dengan langkah-langkah ini, API telah diuji dengan Postman untuk memastikan bahwa setiap endpoint bekerja dengan benar, termasuk autentikasi menggunakan JWT.
Penjelasan Soft Delete
Soft delete adalah teknik menghapus data dengan menandai bahwa data telah dihapus tanpa benar-benar menghapusnya dari database. Alih-alih menghapus baris data secara permanen, sistem akan menambahkan kolom seperti deleted_at
, dan jika kolom ini memiliki nilai, maka data dianggap telah dihapus.
Analogi Soft Delete
Bayangkan sebuah keranjang sampah di komputer. Saat file dihapus, file tidak langsung hilang dari sistem, tetapi dipindahkan ke keranjang sampah. File tersebut tetap ada dan bisa dikembalikan kapan saja. Namun, ketika pengguna mengosongkan keranjang sampah, barulah file benar-benar dihapus dari sistem.
Implementasi Soft Delete Menggunakan GORM
Soft delete di GORM dapat dilakukan dengan menambahkan kolom gorm.DeletedAt
. Ini memungkinkan data tetap ada di database tetapi tidak akan muncul dalam query biasa kecuali diminta secara eksplisit.
Buat model User
dengan fitur soft delete di models/user.go
:
package models
import (
"github.com/google/uuid"
"gorm.io/gorm"
)
type User struct {
ID uuid.UUID `gorm:"type:char(36);primary_key" json:"id"`
Name string `gorm:"not null" json:"name"`
Email string `gorm:"unique;not null" json:"email"`
Password string `gorm:"not null" json:"-"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.ID = uuid.New()
return
}
Kolom DeletedAt
akan otomatis digunakan oleh GORM untuk menandai data yang dihapus.
Menghapus Data dengan Soft Delete
Buat fungsi DeleteUser
di controllers/userController.go
:
func DeleteUser(c *gin.Context) {
userID := c.Param("id")
if err := database.DB.Where("id = ?", userID).Delete(&models.User{}).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete user"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"})
}
Saat user dihapus menggunakan Delete()
, GORM tidak akan menghapusnya dari database, tetapi hanya akan mengisi kolom DeletedAt
dengan timestamp penghapusan.
Mendapatkan Data yang Tidak Dihapus
Secara default, GORM akan otomatis menyaring data yang memiliki nilai DeletedAt
. Jika ingin mendapatkan daftar pengguna aktif saja, gunakan query berikut:
func GetUsers(c *gin.Context) {
var users []models.User
database.DB.Where("deleted_at IS NULL").Find(&users)
c.JSON(http.StatusOK, users)
}
Mendapatkan Data yang Sudah Dihapus
Untuk mengambil data yang sudah dihapus, gunakan query dengan Unscoped()
:
func GetDeletedUsers(c *gin.Context) {
var users []models.User
database.DB.Unscoped().Where("deleted_at IS NOT NULL").Find(&users)
c.JSON(http.StatusOK, users)
}
Mengembalikan Data yang Terhapus (Restore)
Jika pengguna ingin mengembalikan data yang telah dihapus, cukup set DeletedAt
menjadi NULL
:
func RestoreUser(c *gin.Context) {
userID := c.Param("id")
database.DB.Unscoped().Model(&models.User{}).Where("id = ?", userID).Update("deleted_at", nil)
c.JSON(http.StatusOK, gin.H{"message": "User restored successfully"})
}
Dengan menggunakan soft delete, data tetap dapat dipulihkan kapan saja dan tidak benar-benar hilang dari database.
Kesimpulan yang Telah Dipelajari
- Soft Delete sebagai Solusi Aman Soft delete memungkinkan data tetap tersimpan di database dengan hanya menandai sebagai "dihapus", bukan menghapusnya secara permanen. Konsep ini mirip dengan keranjang sampah di komputer, sehingga data masih bisa dipulihkan jika dibutuhkan.
- Dukungan GORM untuk Soft Delete GORM menyediakan fitur bawaan
gorm.DeletedAt
, yang secara otomatis menyaring data yang telah dihapus dari query normal. Jika ingin melihat atau mengembalikan data yang sudah dihapus, dapat menggunakanUnscoped()
. - Keamanan Autentikasi dengan JWT JWT (JSON Web Token) digunakan untuk memastikan bahwa hanya pengguna yang telah login yang bisa mengakses data sensitif. Ini adalah metode autentikasi yang efisien dan aman dalam komunikasi antara frontend dan backend dalam aplikasi berbasis API.
- Pentingnya Hashing Password dengan bcrypt Password harus disimpan dalam bentuk hash untuk meningkatkan keamanan sistem. Dengan menggunakan bcrypt, password pengguna tidak tersimpan dalam bentuk teks biasa, sehingga lebih sulit dicuri atau dibobol oleh pihak yang tidak berwenang.
- Postman sebagai Alat Uji API yang Efektif Postman mempermudah pengujian endpoint API tanpa perlu membangun frontend terlebih dahulu. Dengan fitur seperti menyimpan token JWT dan otomatisasi request, Postman sangat membantu dalam proses pengembangan dan debugging API.
Saran untuk Programmer yang Ingin Belajar Lebih Dalam
- Belajar dengan mentor expert di BuildWithAngga adalah pilihan terbaik bagi programmer yang ingin mempercepat pemahaman dalam membangun proyek nyata.
- Akses kelas selamanya memungkinkan programmer belajar dengan fleksibel tanpa batas waktu.
- Membangun portofolio kerja berkualitas agar lebih siap bersaing dalam dunia industri teknologi.
- Kesempatan magang online yang dibayar memberikan pengalaman langsung dalam proyek nyata dan meningkatkan peluang mendapatkan pekerjaan.
- Beragam benefit tambahan seperti networking dengan profesional, dukungan komunitas, dan pembelajaran berbasis studi kasus membuat proses belajar lebih efektif dan aplikatif.