M
Mr Sugiarto
Developer
29 Aug 2026 4 min read

Sejauh ini setiap handler membangun response JSON-nya sendiri-sendiri lewat fiber.Map{...} berulang kali. Formatnya konsisten secara kebetulan, bukan by design — kalau lupa satu field saja, respons antar endpoint bisa jadi tidak seragam. Di part ini kita rapikan dengan helper response reusable, sekaligus tambahkan penanganan error terpusat.

Helper Response

Buat response/response.go:

package response

import "github.com/gofiber/fiber/v2"

func Success(c *fiber.Ctx, status int, message string, data interface{}) error {
	return c.Status(status).JSON(fiber.Map{
		"message": message,
		"data":    data,
	})
}

func Error(c *fiber.Ctx, status int, message string) error {
	return c.Status(status).JSON(fiber.Map{
		"message": message,
	})
}

func ValidationError(c *fiber.Ctx, errors map[string]string) error {
	return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{
		"message": "validasi gagal",
		"errors":  errors,
	})
}

Refactor PostHandler dan AuthHandler supaya semua respons lewat tiga fungsi ini. Sebagai contoh, PostHandler.Show yang sebelumnya:

func (h *PostHandler) Show(c *fiber.Ctx) error {
	var post models.Post

	id := c.Params("id")
	if err := h.DB.First(&post, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
				"message": "post tidak ditemukan",
			})
		}
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "gagal mengambil data post",
		})
	}

	return c.JSON(fiber.Map{
		"data": post,
	})
}

menjadi lebih ringkas:

func (h *PostHandler) Show(c *fiber.Ctx) error {
	var post models.Post

	id := c.Params("id")
	if err := h.DB.First(&post, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return response.Error(c, fiber.StatusNotFound, "post tidak ditemukan")
		}
		return response.Error(c, fiber.StatusInternalServerError, "gagal mengambil data post")
	}

	return response.Success(c, fiber.StatusOK, "berhasil mengambil data post", post)
}

Pola yang sama diterapkan ke semua method di PostHandler dan AuthHandler — termasuk mengganti manual fiber.Map{"errors": ...} di validation jadi response.ValidationError(c, validation.FormatErrors(err)).

Error Handler Terpusat

Secara default, kalau terjadi panic di dalam handler (misalnya nil pointer dereference), Fiber akan mengirim response HTML generik dan (tergantung skenario) bisa membuat server berhenti merespons. Kita tidak mau itu — API harus selalu balas JSON, bahkan saat terjadi error tak terduga.

Fiber punya dua bagian untuk ini: ErrorHandler di config (menangani error yang di-return dari handler) dan middleware recover (menangkap panic supaya tidak menjatuhkan seluruh proses). Update main.go:

app := fiber.New(fiber.Config{
	ErrorHandler: func(c *fiber.Ctx, err error) error {
		code := fiber.StatusInternalServerError
		message := "terjadi kesalahan pada server"

		var fiberErr *fiber.Error
		if errors.As(err, &fiberErr) {
			code = fiberErr.Code
			message = fiberErr.Message
		}

		return response.Error(c, code, message)
	},
})

app.Use(recover.New())

recover.New() adalah middleware bawaan Fiber (github.com/gofiber/fiber/v2/middleware/recover) yang menangkap panic di handler manapun, mengubahnya jadi error biasa, lalu meneruskannya ke ErrorHandler di atas — jadi walaupun ada bug yang menyebabkan panic, server tetap hidup dan client tetap dapat response JSON yang rapi (500 dengan pesan generik), bukan koneksi putus begitu saja.

ErrorHandler juga otomatis menangani kasus route yang tidak ditemukan (404) — Fiber membungkusnya sebagai *fiber.Error dengan Code: 404, yang oleh handler kita di atas ikut diformat ulang jadi JSON konsisten, bukan format bawaan Fiber.

Testing

Jalankan server, lalu coba akses route yang tidak ada:

curl -i http://127.0.0.1:3000/api/v1/tidak-ada

Responnya sekarang konsisten dengan format API kita:

{"message":"Cannot GET /api/v1/tidak-ada"}

Semua endpoint yang sudah kita buat sebelumnya (Post CRUD, auth) tetap berfungsi sama seperti sebelumnya — hanya format kodenya yang lebih rapi dan konsisten, plus sekarang tahan terhadap panic tak terduga.

Response kita sekarang seragam di seluruh API. Di part berikutnya kita tambahkan pagination dan search ke endpoint Index Post, supaya tidak semua data ditarik sekaligus saat datanya sudah banyak.

M
Mr Sugiarto

Developer

Bagian dari Series: GoFiber Dasar - Belajar Fundamental Lewat REST API Blog

Belajar konsep-konsep dasar GoFiber (routing, handler, GORM, MySQL, JWT auth) dengan membangun REST API blog sederhana dari nol - lengkap dengan auten...

Lihat Series Lengkap