Endpoint GET /posts/{id} yang kita buat sejak Part 2 selalu query ke database setiap kali diakses — padahal detail sebuah post biasanya tidak berubah-ubah tiap detik. Kalau endpoint ini sering diakses (misalnya post yang lagi viral), query database yang sama akan berulang kali dieksekusi tanpa perlu. Di part ini kita akan menambahkan caching sederhana untuk mengurangi beban database.
Cache Driver di Goravel
Goravel sudah menyiapkan cache driver memory secara default di config/cache.go — cocok untuk development maupun aplikasi skala kecil tanpa perlu infrastruktur tambahan seperti Redis:
"default": config.Env("CACHE_STORE", "memory"),
"stores": map[string]any{
"memory": map[string]any{
"driver": "memory",
},
},
Untuk production dengan banyak instance server, biasanya driver ini diganti ke Redis supaya cache-nya konsisten antar instance. Tapi untuk kebutuhan tutorial ini, driver memory sudah cukup untuk memahami konsepnya.
Method Remember
Goravel menyediakan method Remember yang polanya sangat umum dipakai untuk caching: "ambil dari cache kalau ada, kalau tidak ada jalankan fungsi ini lalu simpan hasilnya ke cache."
Remember(key string, ttl time.Duration, callback func() (any, error)) (any, error)
Menerapkan Cache di Method Show
Ubah method Show di app/http/controllers/post_controller.go:
const postCacheTTL = 60 * time.Second
func postCacheKey(id string) string {
return "post:" + id
}
func (r *PostController) Show(ctx http.Context) http.Response {
id := ctx.Request().Route("id")
cached, err := facades.Cache().Remember(postCacheKey(id), postCacheTTL, func() (any, error) {
var post models.Post
if err := facades.Orm().Query().With("User").With("Categories").Where("id", id).First(&post); err != nil {
return nil, err
}
if post.ID == 0 {
return nil, errors.New("post not found")
}
return post, nil
})
if err != nil {
return helpers.Error(ctx, http.StatusNotFound, "Post not found", nil)
}
return helpers.Success(ctx, http.StatusOK, "Post retrieved successfully", cached)
}
Request pertama ke GET /posts/{id} akan tetap query ke database seperti biasa (cache miss), tapi hasilnya disimpan ke cache dengan key post:{id} selama 60 detik. Request berikutnya dalam rentang waktu itu langsung diambil dari cache tanpa menyentuh database sama sekali.
Invalidasi Cache Saat Data Berubah
Masalah klasik soal caching: data yang di-cache bisa jadi stale (basi) kalau post-nya diupdate atau dihapus, tapi cache belum expired. Solusinya, kita hapus cache-nya secara eksplisit begitu ada perubahan.
Di method Update, tambahkan setelah proses update selesai:
facades.Cache().Forget(postCacheKey(id))
Lakukan hal yang sama di method Destroy:
facades.Cache().Forget(postCacheKey(id))
Dengan begini, begitu ada yang mengubah atau menghapus sebuah post, cache untuk post tersebut langsung dibuang — request GET berikutnya akan mengambil data terbaru dari database lagi (dan menyimpannya ulang ke cache).
Testing
Untuk membuktikan cache-nya benar-benar bekerja, kita bisa "mengakali" — ubah data langsung di database (bukan lewat API), lalu cek apakah endpoint masih menampilkan data lama:
# 1. Ambil detail post lewat API, catat title-nya
curl http://127.0.0.1:3000/posts/1
# 2. Ubah title-nya langsung di database (bukan lewat API)
mysql -e "UPDATE posts SET title='Diubah Langsung di DB' WHERE id=1"
# 3. Akses lagi dalam waktu < 60 detik — title yang tampil masih yang LAMA
curl http://127.0.0.1:3000/posts/1
Selama masih dalam rentang cache (60 detik), response akan tetap menampilkan title lama walau data di database sudah berubah — ini membuktikan request kedua benar-benar diambil dari cache, bukan query ulang ke database. Setelah 60 detik lewat, atau kalau perubahan dilakukan lewat endpoint PUT /posts/{id} (yang otomatis memanggil Forget), data terbaru akan langsung muncul.
Penutup
Caching yang kita terapkan di sini masih level dasar — cache per record berdasarkan ID, dengan TTL pendek. Endpoint list (Index) sengaja tidak kita cache di part ini karena kombinasi page, per_page, dan search bisa menghasilkan banyak sekali variasi cache key yang perlu diinvalidasi bersamaan; itu topik yang lebih cocok dibahas terpisah kalau nanti kita mengenal cache tags. Di part berikutnya kita akan membahas queue dan background job untuk pekerjaan yang tidak perlu diproses secara langsung (synchronous) saat request berlangsung.
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