Mobil geliştirmede Cursor Pagination — nedir, prensibi ve gerçekleştirilmesi

Yazar: IT Sectr Yayınlanma: 2026-03-11 Okuma süresi: 9 dk

Cursor Pagination, sıralı bir kayıt kümesinde gezinmek için benzersiz bir imleç kullanan sayfalı veri yükleme yöntemidir. GraphQL Specification (2025)'a göre, imleç tabanlı sayfalama, dinamik verilerle çalışan API'ler için önerilen standarttır. Cursor pagination, Offset yaklaşımının ana dezavantajlarını ortadan kaldırır: eklemeler sırasında kararsızlık ve büyük offsetlerde performans düşüşü.

Önemli Noktalar

  • Cursor Pagination, her kaydın gezinme için benzersiz bir imleç tanımlayıcısına sahip olduğu bir sayfalama yöntemidir.
  • Imleç, bir veri kümesindeki benzersiz konum işaretçisidir (genellikle ID, UUID, timestamp) ve eklemelerde değişmez.
  • Kararlılık — istekler arasında eklenen yeni kayıtlar imleci kaydırmaz, yinelenenleri ve boşlukları ortadan kaldırır.
  • Performans — WHERE id > cursor sorgusu, büyük veri kümelerinde hız kaybetmeden indeksi verimli bir şekilde kullanır.
  • Sınırlama — imleç sayfalama, sayfa numarasına göre gezinmeyi desteklemez (5. sayfaya atlayamazsınız).

Cursor Pagination Nedir?

Cursor Pagination, sunucunun verilerle birlikte özel bir işaretçi — bir imleç — döndürdüğü sayfalı bir yükleme yöntemidir. İstemci, bir sonraki kayıt grubunu almak için bu imleci sonraki istekte kullanır. Imleç, geçerli sayfadaki son öğenin benzersiz tanımlayıcısıdır.

İstemcinin “bana 20 kayıtlı 5. sayfayı ver” dediği Offset sayfalamasının aksine, imleç sayfalama farklı çalışır: “bana ID = 83 olan kayıttan sonra 20 kayıt ver.” Sunucu, WHERE id > 83 ve LIMIT 20 ile bir sorgu çalıştırır. Bu yaklaşım, eklemelerden bağımsız olarak her kaydın tam olarak bir sayfaya düşmesini garanti eder.

Imleç sayfalama kavramı, Relay Connection (GraphQL) spesifikasyonu sayesinde geniş çapta benimsenmiş ve imleç tabanlı sayfalamayı modern API'ler için standart haline getirmiştir. Relay, yanıt biçimini tanımlar: edges (imleçlerle kayıt dizisi), pageInfo (hasNextPage, hasPreviousPage, startCursor, endCursor).

Tarihçe

Imleç sayfalama yeni bir teknik değildir — web'den çok önce veritabanlarında kullanılıyordu. SQL'de buna keyset pagination veya seek method denir. Yöntem, 2015 yılında Relay spesifikasyonunun yayınlanmasından sonra API'lerde popüler hale geldi ve HTTP taşımasında tekdüzelik için imleç biçimini base64 kodlu bir dize olarak resmileştirdi.

Imleç Sayfalama Nasıl Çalışır

Imleç sayfalamanın temel prensibi, sorgunun konumlandırma için bir offset yerine dizinlenmiş bir alanda WHERE koşulu kullanmasıdır. İleri yön için WHERE id > last_id kullanılır; geri yön için WHERE id < first_id kullanılır. B-tree indeksi, imleçten sonraki ilk kaydı O(log n)'de bularak kararlı yanıt süresi sağlar.

sql
-- İmleç '83' sonrası 20 kayıt al
SELECT id, title, created_at
FROM posts
WHERE id < 83
ORDER BY id DESC
LIMIT 20;

-- İmleç '83' ÖNCESİ 20 kayıt al (geri)
SELECT id, title, created_at
FROM posts
WHERE id > 83
ORDER BY id ASC
LIMIT 20;

Imleç Biçimi

Bir imleç basit (bir ID değeri) veya karmaşık (birden çok alandan oluşan) olabilir. Basit imleçler bir kaydın birincil anahtarıdır, örneğin otomatik artan id veya UUID. Bileşik imleçler, benzersiz olmayan alanlara göre sıralama için kullanılır, örneğin (created_at, id), burada id aynı zaman damgalarında benzersizliği garanti eder.

Tipik bir API biçimi, base64 kodlu bir dize olarak imleçtir. Sunucu imleci çözer, değeri çıkarır ve SQL sorgusunu oluşturur. Base64 kodlaması, imlecin iç yapısını istemciden gizler ve geriye dönük uyumluluğu bozmadan biçim değişikliğine izin verir. İstemci, yanıtın endCursor alanında imleçleri alır ve sonraki istekte dize olarak iletir.

İleri ve Geri Gezinme

Imleç sayfalama, çift yönlü gezinmeyi destekler. İleri hareket (next) için geçerli sayfadaki son öğenin imleci kullanılır; geri (previous) için ilk öğenin imleci kullanılır. İstekteki after ve before parametreleri yönü belirler: after imleçten sonraki kayıtları alır, before imleçten önceki kayıtları alır.

Cursor ve Offset Sayfalama

Imleç ve Offset sayfalama arasındaki seçim, bir API tasarlarken önemli mimari kararlardan biridir. Her yöntemin uygulanabilirliğini belirleyen güçlü ve zayıf yönleri vardır. Cursor pagination dinamik verili senaryolarda kazanır; Offset isteğe bağlı gezinme senaryolarında kazanır.

ÖzellikCursorOffset
Eklemede kararlılıkYüksek (yinelenen yok)Düşük (sayfa kayması)
Büyük kümelerde performansO(log n) — kararlıO(n) — büyümeyle düşer
Sayfa numarasına göre gezinmeHayırEvet (page=5)
Uygulama karmaşıklığıOrtaDüşük
REST desteğicursor/before/afterpage/offset
GraphQL desteğiRelay standardıÖnerilmez

Offset Neden Büyük Ölçekte Başarısız Olur

Offset sayfalama, OFFSET konumuna kadar tam tablo taraması yapar. offset=100000'de, LIMIT 20 olsa bile veritabanı 100000 satırı okur ve atlar. MySQL ve PostgreSQL OFFSET'i optimize edemez — bu, SQL'de LIMIT/OFFSET uygulamasının bir özelliğidir. Imleç sayfalama, konumu O(log n)'de bulan bir B-tree indeksi kullanır.

Offset'in ek bir sorunu, geriye doğru sayfalarken kayıtları “atlamasıdır”. Bir kullanıcı 5. sayfayı yüklediyse ve o anda yeni kayıtlar eklendiyse, 6. sayfayı istediğinde 5. sayfadaki kaydı tekrar görecek veya yenilerini kaçıracaktır. Cursor pagination bu senaryoyu tamamen ortadan kaldırır: imleç kümede belirli bir yeri işaret eder ve eklemeler konumu değiştirmez.

Cursor Pagination Uygulaması

Imleç sayfalama uygulamasını backend (Kotlin + Spring) ve istemcide (Android + Retrofit) inceleyelim. Sunucu after, before, limit parametrelerini kabul eder ve imleçler ve pageInfo ile birlikte kayıt listesi döndürür. Tipik bir yanıt, sayfalama arayüzünü yönetmek için hasNextPage ve hasPreviousPage içerir.

kotlin
@GetMapping("/posts")
fun getPosts(
    @RequestParam after: Long?,
    @RequestParam(defaultValue = "20") limit: Int
): CursorResponse<Post> {
    val cursor = after ?: Long.MAX_VALUE
    val posts = repository.findByIdLessThanOrderByIdDesc(
        cursor, PageRequest.of(0, limit)
    )
    val endCursor = posts.lastOrNull()?.id
    return CursorResponse(
        data = posts,
        pageInfo = PageInfo(
            hasNextPage = posts.size == limit,
            endCursor = endCursor
        )
    )
}

Android'de İstemci Uygulaması

İstemcide, imleç sayfalama Paging 3'ten PagingSource aracılığıyla uygulanır ve anahtar bir imleçtir (Long). PagingSource.load, LoadParams.key'i alır — son yüklenen kaydın imleci. LoadResult.Page verileri ve nextKey'i — sonraki sayfanın imleci — döndürür. nextKey = null olduğunda sayfalama tamamlanır.

kotlin
// Retrofit API
interface PostApi {
    @GET("posts")
    suspend fun getPosts(
        @Query("after") after: Long?,
        @Query("limit") limit: Int = 20
    ): CursorResponse<Post>
}

// Imleç anahtarıyla PagingSource
class PostPagingSource(
    private val api: PostApi
) : PagingSource<Long, Post>() {

    override suspend fun load(
        params: LoadParams<Long>
    ): LoadResult<Long, Post> = try {
        val response = api.getPosts(
            after = params.key,
            limit = params.loadSize
        )
        val nextKey = response.pageInfo.endCursor
        LoadResult.Page(
            data = response.data,
            prevKey = null,
            nextKey = nextKey
        )
    } catch (e: Exception) {
        LoadResult.Error(e)
    }
}

Relay ile GraphQL Uygulaması

GraphQL'de, imleç sayfalama Relay Connection deseni aracılığıyla uygulanır. Her türün bir Connection (pageInfo ve edges ile) ve bir Edge (node + cursor) vardır. Sorgu, first, after, last, before parametrelerini iletir. Sunucu imleçlerle birlikte edges dizisi ve hasNextPage/hasPreviousPage ile pageInfo döndürür.

Cursor Pagination Ne Zaman Kullanılmalı

Imleç sayfalama, kayıtların sık sık eklendiği veya silindiği dinamik verilerle çalışan API'ler için önerilir. Klasik örnekler: sosyal ağdaki haber akışı, sohbet mesajları, işlem geçmişi, gönderi yorumları. Tüm bu senaryolarda, tutarlılık ve yinelenen olmaması önemlidir.

  • Sohbet ve mesajlaşma — her yeni mesaj listenin üstüne eklenir. Offset sayfalama her yeni mesajda bozulur.
  • Sosyal ağlar ve akışlar — gönderiler sürekli yayınlanır. Imleç sayfalama, kullanıcının tek bir gönderiyi kaçırmamasını sağlar.
  • Sipariş ve işlem geçmişi — veriler daha az sıklıkta değişir, ancak finansal raporlama için tutarlılık kritiktir.
  • Büyük veri hacimli API'ler — milyonlarca kayıt. Cursor pagination, Offset'in yavaşlamaya başladığı yerde performansı korur.
  • GraphQL API'leri — Relay standardı, spesifikasyona uygunluk için imleç tabanlı sayfalama gerektirir.
  • Sonsuz kaydırmalı mobil uygulamalar — kullanıcı yeni gruplar yükleyerek aşağı kaydırır. Imleç yaklaşımı, yinelenen olmadan akıcı bir kullanıcı deneyimi sağlar.

Imleç Sayfalama Ne Zaman Uygun Değildir

Offset sayfalamanın daha uygun olduğu senaryolar vardır: sayfa numarasına göre gezinme gerektiren yönetim panelleri; sonuçların değişebileceği sayfalamalı arama; 5. sayfaya sabit bağlantı gerektiren raporlar ve analitik. Bu durumlarda, imlecin avantajları uygulama karmaşıklığını aşmaz.

Imleç sayfalama, rastgele bir sayfaya “atlamayı” desteklemez — kullanıcı “Sayfa 5”e tıklayıp oraya gidemez. Bu bir mimari sınırlamadır: toplam sayfa sayısını saymak ayrı bir COUNT sorgusu gerektirir ve büyük tablolar için pahalı olabilir. Bu gibi durumlarda hibrit bir yaklaşım: veriler için imleç + sayfalama için count.

Sıkça Sorulan Sorular

Cursor Pagination'da imleç nedir?

Imleç, veri kümesinde bir konumu işaret eden benzersiz bir kayıt tanımlayıcısıdır. Basit (bir kayıt ID'si) veya bileşik (birden çok alan) olabilir. İstemci, sayfadaki son kaydın imlecini alır ve sonraki grubu almak için sonraki istekte iletir.

Cursor Pagination neden Offset'ten daha iyidir?

Cursor pagination, yeni kayıtlar eklendiğinde kaymaya maruz kalmaz — her öğe tam olarak bir sayfaya düşer. Ayrıca ilk n satırı taramak yerine indeksleri kullanarak büyük hacimlerde hızı korur. Offset daha basittir ancak dinamik veriler için kararsızdır.

Imleç sayfalama GraphQL olmadan uygulanabilir mi?

Evet, imleç sayfalama GraphQL'e bağlı değildir. Herhangi bir REST API'de imleci sorgu parametresi ?after=83&limit=20 olarak ileterek uygulanabilir. Yanıt, endCursor ve hasNextPage ile pageInfo içermelidir — bu, istemcinin iç imleç yapısını bilmeden yüklemeyi yönetmesini sağlar.

Hangi imleç kullanılmalı — ID, UUID veya timestamp?

Otomatik artan ID en uygun seçimdir: tekdüze artar, değişmez, verimli bir şekilde indekslenir. UUID v7 (zaman sıralı) da uygundur. Zaman damgaları aynı anda yinelenenler üretebilir, bu nedenle ID ile birleştirin: (created_at, id) imleç benzersizliğini garanti etmek için.

Imleç sayfalama ile toplam sayfa sayısı nasıl bulunur?

Imleç sayfalama toplam sayfa sayısını sağlamaz — bu onun sınırlamasıdır. Toplam bilgiye ihtiyacınız varsa, aynı filtrelerle ayrı bir COUNT sorgusu çalıştırın. Büyük tablolar için EXPLAIN aracılığıyla yaklaşık sayma veya analitikten önbelleğe alınmış toplam kullanın.

Özet

  • Cursor Pagination, offset yerine benzersiz kayıt tanımlayıcısıyla gezinme sağlayan bir sayfalama yöntemidir.
  • Imleç eklemelerde küme kararlılığını garanti eder: yeni kayıtlar zaten yüklenmiş sayfaları kaydırmaz.
  • Performans B-tree indeksi kullanımı sayesinde büyük veri hacimlerinde yüksek (O(log n)) kalır.
  • Cursor pagination dinamik veriler için uygundur: sohbetler, haber akışları, işlemler, yorumlar.
  • Ana sınırlama sayfa numarasıyla gezinme eksikliği ve rastgele bir sayfaya atlayamama durumudur.
  • Uygulama imleçten sonra WHERE id, after/before parametreleri ve yanıtta pageInfo kullanır.
  • Standart — Relay Connection GraphQL, ancak imleç parametreli REST API'ler de yaygın olarak kullanılır.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun