Offset Pagination — paginasi dengan offset — metode pemuatan data secara halaman melalui HTTP API. Klien mengirimkan parameter offset (pergeseran dari awal) dan limit (ukuran halaman), dan server mengembalikan catatan dari posisi offset. Menurut REST API Tutorial, pendekatan ini banyak digunakan di layanan RESTful karena kesederhanaan implementasinya. Namun, pada volume data yang besar, offset-paginasi kehilangan kinerja karena pemindaian tabel secara penuh hingga posisi yang diperlukan.
Poin Utama
Offset Pagination — adalah metode pembagian data secara halaman, di mana permintaan klien berisi dua parameter: offset (berapa banyak catatan yang dilewati) dan limit (berapa banyak catatan yang dikembalikan). Server menjalankan kueri SQL dengan OFFSET dan LIMIT, melewatkan jumlah baris yang ditentukan, dan mengembalikan kumpulan hasil berukuran tetap.
Metode ini muncul di basis data relasional sebagai cara paling sederhana untuk mengatur navigasi antar halaman dan dipindahkan ke HTTP API seiring perkembangan arsitektur REST. Offset Pagination tidak memerlukan penyimpanan status di server — setiap permintaan bersifat independen dan berisi semua informasi yang diperlukan untuk pengambilan data.
Menurut penelitian desain API oleh Postman (2025), offset-paginasi digunakan di 72% REST API publik, menjadikannya standar dominan meskipun ada keterbatasan kinerja yang diketahui pada volume data besar.
Permintaan REST tipikal dengan Offset Pagination menyertakan parameter kueri offset dan limit. Respons berisi daftar catatan halaman yang diminta dan metadata untuk membangun antarmuka navigasi.
Parameter limit membatasi jumlah catatan yang dikembalikan dan melindungi server serta klien dari beban berlebih. Nilai limit tipikal — dari 10 hingga 50 catatan per halaman tergantung pada kompleksitas data.
data class PageRequest(
val offset: Int,
val limit: Int
)
data class PageResponse<T>(
val items: List<T>,
val total: Int,
val hasMore: Boolean
)
fun RetrofitApi.fetchPage(request: PageRequest): Call<PageResponse<Item>>
Offset Pagination diubah menjadi kueri SQL dengan konstruksi OFFSET dan FETCH NEXT (atau LIMIT di MySQL/SQLite). Server basis data memindai tabel, melewatkan jumlah baris yang sama dengan offset, dan mengembalikan limit baris berikutnya. Semakin besar offset, semakin lama kueri berlangsung.
Masalah kinerja terkait dengan fakta bahwa basis data tidak dapat langsung menuju ke posisi offset — ia harus membaca dan membuang semua baris sebelumnya. Pada offset = 100000 dan limit = 20, DBMS akan membaca 100020 baris dan hanya mengembalikan 20.
SQL — bahasa di mana server menjalankan offset-paginasi. Di PostgreSQL dan MySQL digunakan LIMIT, di SQL Server dan Oracle — OFFSET...FETCH. DBMS yang berbeda mengoptimalkan kueri ini dengan caranya sendiri, tetapi masalah fundamental pemindaian tetap sama.
SELECT id, title, created_at
FROM articles
ORDER BY created_at DESC
OFFSET 100000 ROWS
FETCH NEXT 20 ROWS ONLY;
Konsistensi data — kekurangan utama Offset Pagination saat bekerja dengan kumpulan dinamis. Jika di antara dua permintaan pengguna sebuah catatan baru ditambahkan ke awal tabel, semua catatan yang ada akan bergeser. Pengguna melihat duplikat atau loncatan.
Bayangkan sebuah tabel dengan 100 catatan dengan limit = 20. Di halaman 1, pengguna melihat catatan 1-20. Administrator menambahkan 5 catatan baru. Di halaman 2, pengguna melihat catatan 26-45, bukan 21-40 yang diharapkan — catatan 21-25 terlewatkan dan catatan 21-25 dari set sebelumnya terduplikasi di halaman 1.
Cursor-based pagination — alternatif untuk Offset Pagination, yang menggunakan penunjuk ke catatan terakhir halaman saat ini. Alih-alih offset numerik, klien mengirimkan pengidentifikasi catatan terakhir yang diterima, dan server mengembalikan N catatan berikutnya setelahnya.
Pendekatan cursor-based memecahkan masalah konsistensi: posisi kursor tidak berubah saat penyisipan atau penghapusan, karena kursor merujuk ke catatan tertentu, bukan ke posisi. Namun, ini lebih rumit untuk diimplementasikan — memerlukan bidang yang dapat diurutkan secara unik (biasanya ID atau timestamp).
| Parameter | Offset Pagination | Cursor-based Pagination |
|---|---|---|
| Kesederhanaan | Tinggi — dua parameter numerik | Sedang — memerlukan pengkodean kursor |
| Konsistensi | Rendah — duplikat saat penyisipan | Tinggi — kursor tidak tergantung perubahan |
| Kinerja | Menurun seiring bertambahnya offset | Stabil pada volume berapa pun |
| Lompat ke halaman | Ya — bisa pergi ke halaman mana pun | Tidak — hanya navigasi berurutan |
| Cocok untuk | Tabel <10K catatan, UI dengan nomor halaman | Feed, scroll tak terbatas, set besar |
Pilihan antara pendekatan tergantung pada kebutuhan antarmuka pengguna. Jika diperlukan navigasi dengan nomor halaman dan lompatan langsung — Offset Pagination lebih sederhana. Untuk scroll tak terbatas atau feed berita, kursor lebih disukai.
Keyset pagination — varian dari pendekatan cursor-based, di mana pemfilteran dilakukan berdasarkan kunci unik menggunakan WHERE, bukan OFFSET. Kueri SQL menggunakan kondisi WHERE id > lastId, yang memungkinkan basis data menggunakan indeks tanpa memindai baris yang dibuang.
Menurut PostgreSQL Wiki, keyset pagination dijalankan 100-1000 kali lebih cepat daripada kueri offset pada pergeseran besar, karena pemindaian indeks menggantikan penelusuran tabel penuh. Kelemahan — ketidakmampuan untuk melompat ke halaman sembarangan tanpa penelusuran berurutan.
Offset Pagination optimal untuk kumpulan data kecil dan menengah (hingga 10000 catatan), di mana pengguna membutuhkan antarmuka dengan nomor halaman. Skenario tipikal — panel admin, daftar pesanan, katalog dengan pemfilteran dan paginasi per halaman.
Untuk aplikasi mobile, offset-paginasi cocok saat memuat data historis, di mana penyisipan catatan baru jarang atau tidak mungkin — misalnya, riwayat pesanan pengguna, daftar tugas yang selesai, arsip transaksi. Dalam skenario ini, masalah konsistensi tidak muncul.
Tidak disarankan menggunakan Offset Pagination untuk feed media sosial, daftar komentar, obrolan, dan kumpulan dinamis lain dengan penyisipan sering. Dalam kasus ini, loncatan dan duplikat catatan memperburuk pengalaman pengguna dan memerlukan logika deduplikasi tambahan di klien.
Paginasi hibrida menggabungkan offset dan cursor: permintaan pertama menggunakan offset untuk menampilkan halaman awal, dan permintaan berikutnya menggunakan cursor untuk memuat scroll tak terbatas. Pendekatan ini diimplementasikan di Instagram dan Twitter, di mana halaman pertama dimuat melalui cursor, tetapi offset digunakan untuk menghitung posisi saat kembali ke tampilan sebelumnya.
Implementasi pendekatan hibrida memerlukan penyimpanan posisi virtual pengguna di klien dan koordinasi dua mekanisme paginasi di server. Menurut blog Instagram Engineering, tim mereka menggunakan cursor-based pagination dengan bidang tambahan startCursor yang menggantikan offset untuk pemuatan awal.
Aplikasi mobile menggunakan Offset Pagination bersama dengan Retrofit/OkHttp di Android dan URLSession/Combine di iOS. Pola tipikal — memuat halaman berikutnya saat scroll ke akhir daftar melalui RecyclerView.OnScrollListener atau UICollectionView prefetching.
Implementasi offset-paginasi di klien mobile mencakup tiga komponen: manajer paginasi (menyimpan offset saat ini dan hasMore), adaptor daftar (menampilkan elemen dan indikator pemuatan), dan repositori (menjalankan permintaan dan menangani kesalahan). Android Jetpack menawarkan Paging 3 Library, yang mendukung baik offset maupun cursor-based pagination secara langsung.
Paging 3 — pustaka Android Jetpack untuk pemuatan data secara halaman. Pustaka ini mengenkapsulasi logika paginasi, termasuk pelacakan offset, manajemen status pemuatan, dan pemuatan otomatis saat scroll. PagingSource mendefinisikan kunci untuk halaman berikutnya dan sebelumnya.
class OffsetPagingSource(
private val api: ApiService,
private val limit: Int = 20
) : PagingSource<Int, Item>() {
override suspend fun load(
params: LoadParams<Int>
): LoadResult<Int, Item> {
val offset = params.key ?: 0
return try {
val response = api.getItems(offset, limit)
LoadResult.Page(
data = response.items,
prevKey = null,
nextKey = if (response.hasMore) offset + limit else null
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
PagingSource mendefinisikan kunci prevKey dan nextKey untuk navigasi halaman. Pada offset-paginasi, prevKey selalu null (tidak bisa pergi ke halaman sebelumnya tanpa menyimpan riwayat), dan nextKey bertambah sebesar limit setiap kali pemuatan, hingga server mengembalikan hasMore = false. Ini adalah model yang sederhana dan dapat diprediksi untuk daftar mobile.
Kesalahan pertama — mengandalkan urutan catatan tanpa pengurutan. Offset Pagination memerlukan pengurutan ORDER BY yang stabil pada bidang unik. Tanpa itu, DBMS dapat mengembalikan catatan dalam urutan sembarang, yang menyebabkan duplikat acak dan loncatan antar halaman.
Kesalahan kedua — menggunakan offset untuk menghitung nomor halaman di antarmuka. Rumus page = offset / limit + 1 hanya berfungsi dengan syarat tidak ada catatan yang dihapus atau ditambahkan di antara pemuatan. Dengan data dinamis, nomor halaman menjadi tidak akurat, dan pengguna melihat informasi yang salah.
Kesalahan ketiga — mengabaikan batas waktu kueri dengan offset besar. Pada offset di atas 100000, kueri dapat berlangsung puluhan detik, memblokir antarmuka dan menghabiskan sumber daya server. Disarankan untuk menetapkan nilai offset maksimum di tingkat API (misalnya, 10000) dan menggunakan cursor-based pagination untuk volume besar.
Kesalahan keempat — tidak menambahkan total count ke respons. Tanpa jumlah total catatan, klien tidak dapat menampilkan jumlah halaman dan mengimplementasikan paginasi dengan nomor. Namun, menghitung COUNT(*) pada tabel besar juga mahal — untuk kumpulan di atas 100000 catatan, gunakan perkiraan atau batasi nilai maksimum total.
Pertanyaan yang Sering Diajukan
Offset menggunakan pergeseran numerik (offset) untuk melewatkan catatan, sementara cursor menggunakan penunjuk ke catatan terakhir halaman sebelumnya. Offset lebih sederhana dalam implementasi, tetapi menderita duplikat saat penyisipan dan kehilangan kinerja pada pergeseran besar. Cursor stabil pada setiap perubahan data.
Offset paginasi tidak efisien pada offset di atas 10000 catatan karena pemindaian tabel penuh. Ini juga tidak cocok untuk kumpulan dinamis (feed, obrolan) di mana catatan baru muncul di antara permintaan — pengguna melihat loncatan dan duplikat saat navigasi.
Limit optimal tergantung pada ukuran catatan dan kecepatan jaringan — dari 10 hingga 50 elemen per halaman. Untuk daftar dengan gambar besar, gunakan limit = 10-15, untuk data teks — 20-50. Selalu izinkan klien untuk menentukan limit sendiri dengan batasan maksimum di server (biasanya 100).
Untuk mengatasi duplikat, gunakan deduplikasi di klien berdasarkan ID unik, terapkan pengurutan stabil pada bidang unik, atau beralih ke cursor-based pagination. Android Paging 3 mendukung kunci untuk deduplikasi otomatis elemen daftar.
Ya, GraphQL mendukung offset-paginasi melalui argumen offset dan limit dalam kueri, meskipun spesifikasi Relay merekomendasikan pendekatan cursor-based. Pustaka Apollo GraphQL dan Relay menawarkan dukungan bawaan untuk offset-paginasi dengan manajemen status halaman otomatis.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga