Endpoint GET /api/v1/posts kita sejauh ini selalu mengembalikan semua post sekaligus. Baik-baik saja untuk data dummy, tapi kalau nanti sudah ada ribuan post, ini jelas tidak efisien — response jadi besar dan query database jadi berat. Di part ini kita tambahkan pagination dan pencarian sederhana.
Membaca Query Parameter
Fiber menyediakan c.Query("nama", "default") untuk membaca query string dengan nilai default kalau tidak dikirim. Update PostHandler.Index:
func (h *PostHandler) Index(c *fiber.Ctx) error {
page, err := strconv.Atoi(c.Query("page", "1"))
if err != nil || page < 1 {
page = 1
}
perPage, err := strconv.Atoi(c.Query("per_page", "10"))
if err != nil || perPage < 1 || perPage > 100 {
perPage = 10
}
search := c.Query("search", "")
query := h.DB.Model(&models.Post{})
if search != "" {
like := "%" + search + "%"
query = query.Where("title LIKE ? OR body LIKE ?", like, like)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return response.Error(c, fiber.StatusInternalServerError, "gagal mengambil data post")
}
var posts []models.Post
offset := (page - 1) * perPage
if err := query.Order("id desc").Offset(offset).Limit(perPage).Find(&posts).Error; err != nil {
return response.Error(c, fiber.StatusInternalServerError, "gagal mengambil data post")
}
totalPages := int(math.Ceil(float64(total) / float64(perPage)))
return c.JSON(fiber.Map{
"message": "berhasil mengambil data post",
"data": posts,
"meta": fiber.Map{
"page": page,
"per_page": perPage,
"total": total,
"total_pages": totalPages,
},
})
}
Beberapa hal penting:
pagedanper_pagedivalidasi manual (bukan lewatgo-playground/validator, karena ini query parameter, bukan body request) — kalau nilainya tidak valid atau negatif, fallback ke default.per_pagedibatasi maksimal100supaya client tidak bisa minta seluruh tabel sekaligus lewatper_page=999999.query := h.DB.Model(&models.Post{})dibuat sekali, lalu dipakai ulang untukCount(total sebelum pagination) danFind(data untuk halaman ini) — supaya filtersearchkonsisten di kedua query.OffsetdanLimitadalah cara GORM melakukan pagination di level SQL (LIMIT ... OFFSET ...), jauh lebih efisien daripada ambil semua data lalu di-slice di sisi aplikasi.
Testing
# Ambil 3 post pertama
curl "http://127.0.0.1:3000/api/v1/posts?page=1&per_page=3"
# Halaman kedua
curl "http://127.0.0.1:3000/api/v1/posts?page=2&per_page=3"
# Cari post yang mengandung kata tertentu di title atau body
curl "http://127.0.0.1:3000/api/v1/posts?search=GoFiber"
Response sekarang punya bagian meta di samping data:
{
"message": "berhasil mengambil data post",
"data": [ ... ],
"meta": {
"page": 1,
"per_page": 2,
"total": 15,
"total_pages": 8
}
}
Field meta ini yang biasanya dipakai frontend untuk menampilkan navigasi halaman (misalnya tombol "Next"/"Previous" atau nomor halaman).
Endpoint list Post kita sekarang siap menangani data dalam jumlah besar. Di part berikutnya kita hubungkan Post dengan User sebagai penulisnya (author) — sekaligus menutup celah otorisasi yang sudah kita catat sejak Part 9: siapapun yang login masih bisa edit/hapus post siapa saja.
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