M
Mr Sugiarto
Developer
01 Sep 2026 5 min read

Perhatikan lagi alur Register yang kita buat sejak Part 7: hash password, simpan user, generate access token, generate refresh token — semuanya diproses satu per satu, berurutan, sebelum response dikirim ke client. Kalau di tengah proses itu kita tambahkan pekerjaan yang lama (misalnya kirim email selamat datang lewat pihak ketiga yang responnya bisa berdetik-detik), request Register jadi ikut lambat. Di part ini kita akan memindahkan pekerjaan semacam itu ke queue, supaya diproses di belakang layar tanpa membuat client menunggu.

Konsep Queue di Goravel

Alih-alih pekerjaan dieksekusi langsung di dalam request, kita "titipkan" pekerjaan itu sebagai job ke antrean. Ada proses terpisah — worker — yang bertugas mengambil job dari antrean dan menjalankannya satu per satu, independen dari siklus request/response HTTP.

Goravel mendukung beberapa driver queue, dikonfigurasi di config/queue.go:

"default": config.Env("QUEUE_CONNECTION", "sync"),
"connections": map[string]any{
	"sync": map[string]any{
		"driver": "sync",
	},
	"database": map[string]any{
		"driver":     "database",
		"connection": "mysql",
		"queue":      "default",
		"concurrent": 1,
	},
},

Perlu diperbaiki satu hal di sini: nilai connection bawaannya "postgres", padahal project kita pakai MySQL sejak awal. Ganti jadi "mysql" supaya driver database benar-benar nyambung ke database kita.

Dengan driver sync (default), job dieksekusi langsung saat itu juga, di thread yang sama — cocok untuk development karena sederhana, tapi tidak benar-benar asynchronous. Untuk melihat efek queue yang sesungguhnya, kita perlu pindah ke driver database, yang menyimpan job ke tabel jobs dan baru diproses saat worker berjalan.

Membuat Job

go run . artisan make:job SendWelcomeNotificationJob

Isi app/jobs/send_welcome_notification_job.go:

package jobs

import (
	"fmt"

	"github.com/goravel/framework/facades"
)

type SendWelcomeNotificationJob struct {
}

func (r *SendWelcomeNotificationJob) Signature() string {
	return "send_welcome_notification"
}

func (r *SendWelcomeNotificationJob) Handle(args ...any) error {
	if len(args) < 2 {
		return fmt.Errorf("send_welcome_notification: expected 2 args, got %d", len(args))
	}

	name, _ := args[0].(string)
	email, _ := args[1].(string)

	facades.Log().Infof("Sending welcome notification to %s <%s>", name, email)

	return nil
}

Untuk tutorial ini, "mengirim notifikasi" cukup kita simulasikan lewat log — supaya efek asynchronous-nya mudah diamati tanpa perlu setup layanan email sungguhan.

Mendaftarkan Job

Semua job harus didaftarkan di app/providers/queue_service_provider.go:

func (receiver *QueueServiceProvider) Jobs() []queue.Job {
	return []queue.Job{
		&jobs.SendWelcomeNotificationJob{},
	}
}

Dispatch Job dari Register

Di UserController.Register, setelah user berhasil dibuat:

if err := facades.Queue().Job(&jobs.SendWelcomeNotificationJob{}, []queue.Arg{
	{Type: "string", Value: user.Name},
	{Type: "string", Value: user.Email},
}).Dispatch(); err != nil {
	facades.Log().Errorf("Failed to dispatch welcome notification job: %v", err)
}

Dispatch() menaruh job ke antrean sesuai driver yang aktif. Kalau gagal dispatch, kita cukup catat errornya ke log — jangan sampai kegagalan notifikasi ikut menggagalkan proses registrasi user, karena keduanya adalah concern yang berbeda.

Menjalankan Worker

Framework Goravel versi yang kita pakai belum menyediakan command queue:work bawaan (baru ada queue:failed dan queue:retry), jadi kita buat sendiri. Buat app/console/commands/queue_work_command.go:

package commands

import (
	"github.com/goravel/framework/contracts/console"
	"github.com/goravel/framework/contracts/console/command"
	"github.com/goravel/framework/facades"
)

type QueueWorkCommand struct {
}

func NewQueueWorkCommand() *QueueWorkCommand {
	return &QueueWorkCommand{}
}

func (r *QueueWorkCommand) Signature() string {
	return "queue:work"
}

func (r *QueueWorkCommand) Description() string {
	return "Start processing jobs on the queue as a daemon"
}

func (r *QueueWorkCommand) Extend() command.Extend {
	return command.Extend{
		Category: "queue",
	}
}

func (r *QueueWorkCommand) Handle(ctx console.Context) error {
	ctx.Info("Queue worker started. Press Ctrl+C to stop.")

	return facades.Queue().Worker().Run()
}

Daftarkan command ini di app/console/kernel.go:

func (kernel Kernel) Commands() []console.Command {
	return []console.Command{
		commands.NewQueueWorkCommand(),
	}
}

Testing

Set QUEUE_CONNECTION=database di .env supaya job benar-benar antre (bukan langsung dieksekusi seperti driver sync). Kita butuh dua terminal: satu menjalankan server seperti biasa, satu lagi khusus menjalankan worker.

Terminal 1 — server seperti biasa:

go run .

Terminal 2 — worker:

go run . artisan queue:work

Sekarang coba POST /register. Kalau kita cek tabel jobs segera setelah request selesai (sebelum worker sempat memproses), job masih ada di sana:

SELECT id, queue, attempts FROM jobs;

Begitu worker (terminal 2) mengambil giliran, akan muncul log seperti ini di terminal worker:

send_welcome_notification  RUNNING
send_welcome_notification  DONE

Dan pesan "Sending welcome notification to ..." akan muncul di file log (storage/logs/goravel-{tanggal}.log), bukan langsung saat request Register selesai — membuktikan proses ini benar-benar berjalan asynchronous, terpisah dari request HTTP.

Penutup

Pola ini bisa dipakai untuk pekerjaan berat lain yang tidak perlu langsung selesai saat request berlangsung — generate laporan, resize gambar, kirim notifikasi push, dan sejenisnya. Di part berikutnya kita akan membahas rate limiting, supaya API kita punya proteksi dari penyalahgunaan atau serangan brute-force.

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
Artikel Sebelumnya
Part 18 - Caching Response