M
Mr Sugiarto
Developer
29 Aug 2026 4 min read

Sampai sekarang, semua user yang sudah login punya hak akses yang sama persis. Padahal di aplikasi nyata, biasanya ada perbedaan peran — misalnya user biasa cuma bisa mengelola post miliknya sendiri, sementara admin punya akses lebih luas, seperti mengelola kategori atau menghapus post/comment siapa saja. Di part ini kita akan menambahkan sistem role sederhana: user dan admin.

Menambahkan Kolom Role

go run . artisan make:migration add_role_to_users_table
package migrations

import (
	"github.com/goravel/framework/contracts/database/schema"
	"github.com/goravel/framework/facades"
)

type M20260823000001AddRoleToUsersTable struct{}

func (r *M20260823000001AddRoleToUsersTable) Signature() string {
	return "20260823000001_add_role_to_users_table"
}

func (r *M20260823000001AddRoleToUsersTable) Up() error {
	if facades.Schema().HasTable("users") && !facades.Schema().HasColumn("users", "role") {
		return facades.Schema().Table("users", func(table schema.Blueprint) {
			table.String("role", 20).Default("user")
		})
	}

	return nil
}

func (r *M20260823000001AddRoleToUsersTable) Down() error {
	return facades.Schema().Table("users", func(table schema.Blueprint) {
		table.DropColumn("role")
	})
}

Update app/models/user.go:

Role string `json:"role" gorm:"default:user"`

Tag gorm:"default:user" ini penting — tanpanya, GORM akan tetap mengirim string kosong "" saat insert (bukan mengandalkan default value di level database), sehingga kolom role user baru akan kosong, bukan "user". Sebagai jaring pengaman tambahan, kita juga set eksplisit di UserController.Register:

user := models.User{
	Name:     ctx.Request().Input("name"),
	Email:    ctx.Request().Input("email"),
	Password: string(hashed),
	Role:     "user",
}

Helper IsAdmin

Tambahkan fungsi IsAdmin di app/helpers/auth_helper.go:

func IsAdmin(ctx http.Context) bool {
	var user models.User
	if err := facades.Orm().Query().Where("id", AuthUserID(ctx)).First(&user); err != nil || user.ID == 0 {
		return false
	}

	return user.Role == "admin"
}

Perhatikan: fungsi ini mengambil role langsung dari database, bukan dari JWT claim. Alternatif lain sebenarnya bisa saja menyisipkan role ke dalam claim JWT saat token diterbitkan — lebih cepat karena tidak perlu query tambahan, tapi konsekuensinya kalau role user berubah (misal di-promote jadi admin), perubahan itu baru berlaku setelah user login ulang (karena token lama masih membawa role lama). Dengan query langsung ke database seperti di atas, perubahan role langsung berlaku di request berikutnya tanpa perlu re-login. Untuk aplikasi skala kecil seperti tutorial ini, trade-off query tambahan per request masih cukup wajar.

Middleware AdminMiddleware

package middleware

import (
	"myblog/app/helpers"

	"github.com/goravel/framework/contracts/http"
)

func AdminMiddleware() http.Middleware {
	return func(ctx http.Context) {
		if !helpers.IsAdmin(ctx) {
			ctx.Request().AbortWithStatusJson(http.StatusForbidden, http.Json{
				"success": false,
				"message": "Admin access required",
			})
			return
		}

		ctx.Request().Next()
	}
}

Middleware ini harus dijalankan setelah JwtMiddleware, karena bergantung pada user_id yang sudah disimpan JwtMiddleware ke context.

Menerapkan ke Route

Kita jadikan endpoint pembuatan kategori sebagai contoh fitur khusus admin — masuk akal karena taksonomi konten biasanya dikelola terpusat, bukan oleh sembarang user:

facades.Route().Middleware(middleware.JwtMiddleware(), middleware.AdminMiddleware()).Group(func(router route.Router) {
	router.Post("/categories", categoryController.Store)
})

Goravel mendukung beberapa middleware sekaligus dalam satu pemanggilan Middleware(...) — dieksekusi berurutan sesuai urutan argumennya.

Admin Bisa Override Kepemilikan

Selain endpoint khusus admin, kita juga longgarkan aturan kepemilikan yang dibuat di Part 11 dan Part 12: admin boleh mengubah/menghapus post atau comment siapa saja, tidak terbatas miliknya sendiri. Di PostController, ubah pengecekan kepemilikan menjadi:

if post.UserID != helpers.AuthUserID(ctx) && !helpers.IsAdmin(ctx) {
	return helpers.Error(ctx, http.StatusForbidden, "You are not allowed to update this post", nil)
}

Lakukan hal yang sama di method Destroy pada PostController dan CommentController.

Testing

  1. Register user biasa, coba POST /categories — harus dapat 403 Admin access required.
  2. Ubah role user tersebut jadi admin langsung di database (untuk tutorial ini kita lakukan manual lewat query SQL, di aplikasi nyata biasanya lewat panel admin terpisah):
UPDATE users SET role = 'admin' WHERE email = 'user@example.com';
  1. Coba lagi POST /categories dengan token yang sama (tidak perlu login ulang) — sekarang harus berhasil, karena role dicek langsung dari database setiap request.
  2. Coba juga hapus post/comment milik user lain pakai akun admin — harus berhasil, berbeda dengan user biasa yang akan kena 403.

Penutup

Sistem role ini masih sederhana — hanya dua level (user dan admin) dengan pengecekan manual di tiap endpoint. Untuk kebutuhan lebih kompleks (banyak role, permission granular per fitur), biasanya dipakai sistem policy/gate yang lebih terstruktur. Di part berikutnya kita akan membahas topik yang tidak kalah penting: menulis automated test untuk memastikan semua endpoint yang sudah kita buat tetap bekerja dengan benar.

M
Mr Sugiarto

Developer

Bagian dari Series: Seri Tutorial Belajar Framework Goravel Rest API untuk Pemula

Overview Seri ini membahas cara membangun REST API menggunakan Goravel, framework web berbasis Golang yang terinspirasi dari Laravel. Materi mencakup...

Lihat Series Lengkap