REST API'lerde sayfalama, istemcinin tek seferde alması gereken veri miktarını sınırlayarak performansı artırır ve sunucu yükünü azaltır. Doğru sayfalama stratejisi seçilmediğinde, kullanıcı deneyimi düşebilir ve API'niz tutarsız sonuçlar döndürebilir. Bu yazıda en yaygın üç sayfalama yöntemi olan offset, cursor ve keyset (seek) pagination'ı derinlemesine inceliyoruz.
Sayfalama Nedir ve Neden Gereklidir?
Sayfalama, bir API'den dönen büyük veri kümelerini daha küçük, yönetilebilir parçalara (sayfalara) bölme işlemidir. Örneğin, binlerce kullanıcıyı listeleyen bir endpoint düşünün; hepsini tek seferde döndürmek hem ağ trafiğini şişirir hem de sunucuda gereksiz yük oluşturur. Sayfalama sayesinde istemci yalnızca ihtiyacı olan veri alt kümesini talep eder. Bu, performansı optimize etmek, ağ bant genişliğini korumak ve kullanıcı arayüzünde kesintisiz deneyim sağlamak için kritiktir.
Öte yandan, sayfalama yaparken hata yönetimi ve hız sınırlandırma gibi konular da önem kazanır. Örneğin, REST API'de Hata Yönetimi makalemizde, geçersiz sayfa parametrelerine doğru HTTP durum kodlarıyla nasıl yanıt vereceğinizi bulabilirsiniz. Ayrıca Backend API'lerde Hız Sınırlandırma yazısı, çok fazla istek geldiğinde API'nizi koruma altına almanıza yardımcı olur.
Offset Tabanlı Sayfalama: Nasıl Çalışır, Avantajları ve Dezavantajları
Offset sayfalama en yaygın ve basit yöntemdir. İstemci, offset (atlanacak kayıt sayısı) ve limit (döndürülecek kayıt sayısı) parametrelerini gönderir. Örneğin, GET /users?offset=20&limit=10 isteği 21. kayıttan itibaren 10 kullanıcı döndürür.
Avantajları
- Uygulaması kolaydır; çoğu veritabanı ve ORM (Örn: SQL, MongoDB) doğrudan
OFFSETveLIMITdestekler. - İstemci rastgele bir sayfaya atlayabilir (örneğin 50. sayfa).
Dezavantajları
- Veri tutarlılığı sorunu: Sayfalama sırasında yeni kayıt eklenir veya silinirse, aynı offset değeri farklı sonuçlar döndürebilir (kayma etkisi).
- Performans: Offset büyüdükçe veritabanı, atlanan satırları okumak zorunda kaldığı için sorgu gecikmesi artar. Örneğin
OFFSET 100000çok yavaş olabilir.
Cursor Tabanlı Sayfalama: Nasıl Çalışır, Avantajları ve Dezavantajları
Cursor sayfalama, her sayfada bir imleç (cursor) kullanarak bir sonraki sayfanın başlangıç noktasını belirler. İstemci, genellikle bir opak token (örneğin base64 kodlu bir tanımlayıcı) alır ve sonraki istekte bunu cursor parametresi olarak gönderir. Facebook Graph API, Twitter API ve GitHub API gibi büyük servisler bu yöntemi kullanır.
Avantajları
- Tutarlılık: Sayfalama sırasında yapılan ekleme/silmeler sonucu etkilemez; aynı cursor her zaman aynı sonraki kayıtları gösterir.
- Performans: Cursor, indekslenmiş bir alana (örneğin id, oluşturulma tarihi) dayandığı için offset'e göre çok daha hızlıdır, büyük veri kümelerinde bile.
Dezavantajları
- İstemci rastgele bir sayfaya atlayamaz; yalnızca sıralı gezinme mümkündür.
- Uygulaması offset'e göre biraz daha karmaşıktır; imleç işleme ve döndürme mantığı kurulmalıdır.
Keyset Sayfalama (Seek Pagination): Nasıl Çalışır ve Ne Zaman Kullanılmalı?
Keyset sayfalama, cursor yönteminin bir varyasyonudur. Bir önceki sayfadaki son kaydın belirli bir anahtar alanını (örneğin id veya created_at) kullanarak sonraki kayıtları getirir. Örnek istek: GET /users?after_id=100&limit=10. Veritabanı WHERE id > 100 koşulu ile sorgulanır.
Avantajları
- Cursor kadar tutarlı ve performanslıdır; indeks üzerinde
>,<kullandığı için büyük offset sorununu tamamen ortadan kaldırır. - Uygulaması çoğu durumda cursor'dan daha basittir, çünkü ekstra token üretimi gerektirmez.
Dezavantajları
- Yalnızca tekil indisli alanlarla çalışır (genellikle id).
- Filtreleme ve sıralama değiştiğinde keyset anlamını yitirir; birden çok sıralama koşulu için uygulanması zorlaşır.
Hangi Sayfalama Yöntemini Seçmelisiniz?
| Kriter | Offset | Cursor | Keyset |
|---|---|---|---|
| Uygulama kolaylığı | Çok kolay | Orta | Kolay (basit indeks) |
| Rastgele sayfa erişimi | Var | Yok | Yok |
| Veri tutarlılığı | Düşük | Yüksek | Yüksek |
| Büyük veri performansı | Düşük | Yüksek | Yüksek |
| Karmaşık sıralama desteği | Tam | Sınırlı (tek nokta) | Zor (birden çok anahtar gerekir) |
Öneri: Küçük ölçekli API'ler veya yönetim panelleri için offset yeterlidir. Büyük ölçekli, sürekli güncellenen veriler (sosyal medya akışı, loglar vb.) için cursor veya keyset tercih edilmelidir. Eğer veritabanınızda doğal bir tek birincil anahtar varsa (auto-increment id, UUID), keyset sayfalama hem basit hem etkilidir. Aksi takdirde cursor kullanın.
Sayfalama Yaparken Dikkat Edilmesi Gerekenler
- Varsayılan limit ve maksimum limit belirleyin: Varsayılan limit küçük (örn. 20-50), maksimum limit geniş (örn. 1000) olsun. Aşırı limit isteklerini reddetmek için
413 Payload Too Largedöndürebilirsiniz. - Toplam kayıt sayısını döndürme: Bazı durumlarda istemci toplam sayfa sayısını bilmek ister. Ancak büyük tablolarda
COUNT(*)pahalıdır. Bunun için ayrı bir endpoint veya özel bir yanıt alanı (örneğinX-Total-Count) ekleyebilirsiniz. - Sayfalama bilgilerini HTTP header'larında taşıyın:
Linkheader'ı ile sonraki ve önceki sayfa URL'lerini belirtmek RESTful bir yaklaşımdır. AyrıcaX-Next-Cursorgibi özel header'lar da kullanılabilir. - Hata durumlarını yönetin: Geçersiz offset, cursor veya keyset değerleri için
400 Bad Requestdönün. Çok büyük offset veya eski cursor için410 Goneda kullanılabilir.
Sayfalama stratejinizi belirledikten sonra, API güvenliği için JWT Kimlik Doğrulama makalesini inceleyerek endpoint'lerinizi koruma altına alabilirsiniz.
Örnek Kod Parçacıkları (Node.js ve PostgreSQL)
Aşağıda üç yöntemin nasıl uygulanabileceğine dair örnekler görebilirsiniz.
Offset Sayfalama
app.get('/users', async (req, res) => {
const offset = parseInt(req.query.offset) || 0;
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
const users = await db.query('SELECT * FROM users ORDER BY id OFFSET $1 LIMIT $2', [offset, limit]);
res.json({ data: users.rows, offset, limit });
});
Cursor Sayfalama
app.get('/users', async (req, res) => {
const cursor = req.query.cursor; // base64 kodlu, örnek: 'eyJsYXN0SWQiOjEwMH0='
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
let where = '';
if (cursor) {
const decoded = JSON.parse(Buffer.from(cursor, 'base64').toString());
where = `WHERE id > ${decoded.lastId}`;
}
const users = await db.query(`SELECT * FROM users ORDER BY id ${where} LIMIT $1`, [limit + 1]);
const hasMore = users.rows.length > limit;
if (hasMore) users.rows.pop();
const nextCursor = hasMore ? Buffer.from(JSON.stringify({ lastId: users.rows[users.rows.length-1].id })).toString('base64') : null;
res.json({ data: users.rows, nextCursor });
});
Keyset Sayfalama
app.get('/users', async (req, res) => {
const afterId = parseInt(req.query.after_id) || 0;
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
const users = await db.query('SELECT * FROM users WHERE id > $1 ORDER BY id LIMIT $2', [afterId, limit]);
const nextAfterId = users.rows.length ? users.rows[users.rows.length-1].id : null;
res.json({ data: users.rows, nextAfterId });
});
Bu örnekler, sayfalama mantığını anlamanız için basitleştirilmiştir. Gerçek uygulamalarda güvenlik açıklarına karşı parametre doğrulama, SQL enjeksiyon koruması ve uygun hata yönetimi eklemeyi unutmayın. Ayrıca sayfalama parametrelerini düzenli olarak test etmek için hız sınırlandırma tekniklerini uygulayarak API'nizi koruyun.
Sık Sorulan Sorular
Offset ve cursor sayfalama arasındaki ana fark nedir?
Offset sayfalama, atlanacak kayıt sayısını (offset) belirleyerek çalışır; ancak veri ekleme/silme işlemlerinde kayma etkisi yaşanabilir. Cursor sayfalama ise bir imleç (cursor) kullanarak bir sonraki sayfanın başlangıç noktasını belirler; bu sayede veri değişse bile tutarlı sonuçlar alınır ve büyük offset sorguları olmadığı için performans daha iyidir.
Keyset sayfalama (seek pagination) ne demektir?
Keyset sayfalama, bir önceki sayfadaki son kaydın belirli bir anahtar alanını (örneğin id) kullanarak sonraki kayıtları getiren bir yöntemdir. 'WHERE id > son_id' gibi bir sorgu kullanılır. Bu yöntem, cursor kadar tutarlı ve hızlıdır, ancak yalnızca tek bir sıralama alanıyla kolayca uygulanabilir.
Sayfalama için hangi HTTP header'ları kullanmalıyım?
Yaygın olarak Link, X-Total-Count, X-Next-Cursor ve X-Prev-Cursor header'ları kullanılır. Link header'ı sonraki ve önceki sayfa URL'lerini belirtmek için RESTful bir standarttır. X-Total-Count toplam kayıt sayısını, X-Next-Cursor ise sonraki sayfa için cursor değerini taşıyabilir.
Sonsuz kaydırma (infinite scroll) için hangi sayfalama yöntemi daha uygundur?
Sonsuz kaydırma için cursor veya keyset sayfalama daha uygundur. Çünkü kullanıcı aşağı kaydırdıkça yeni veriler eklenir ve rastgele sayfa atlama gereksinimi yoktur. Ayrıca bu yöntemler veri tutarlılığını korur ve büyük veri kümelerinde offset tabanlı yönteme göre çok daha iyi performans sunar.






