REST API'de Sayfalama Nasıl Uygulanır (Adım Adım Kılavuz)

REST API'lerde büyük veri kümeleri için sayfalama önemlidir. Tek seferde çok fazla veri göndermek performans sorunlarına yol açar. Sayfalama, veriyi parçalara ayırır.

Efe Demir

Efe Demir

5 June 2025

REST API'de Sayfalama Nasıl Uygulanır (Adım Adım Kılavuz)

Kaynak listesi döndüren REST API'leri oluştururken, büyük veri kümelerini nasıl yöneteceğinizi düşünmek çok önemlidir. Binlerce, hatta milyonlarca kaydı tek bir API yanıtında döndürmek pratik değildir ve önemli performans sorunlarına, hem sunucu hem de istemci için yüksek bellek tüketimine ve kötü bir kullanıcı deneyimine yol açabilir. Sayfalama, bu soruna standart bir çözümdür. Büyük bir veri kümesini, daha sonra sırayla sunulan "sayfalar" adı verilen daha küçük, yönetilebilir parçalara ayırmayı içerir. Bu eğitim, REST API'lerinizde çeşitli sayfalama stratejilerini uygulamanın teknik adımları boyunca size rehberlik edecektir.

💡
Harika API Dokümantasyonu oluşturan harika bir API Test aracı mı arıyorsunuz?

Geliştirici Ekibinizin maksimum verimlilikle birlikte çalışması için entegre, Hepsi Bir Arada bir platform mu istiyorsunuz?

Apidog tüm taleplerinizi karşılıyor ve Postman'in yerini çok daha uygun bir fiyata alıyor!
button

Sayfalamanın Neden Önemli Olduğu?

Uygulama detaylarına girmeden önce, sayfalama özelliğinin, kaynak koleksiyonlarıyla ilgilenen API'ler için neden vazgeçilmez bir özellik olduğuna kısaca değinelim:

  1. Performans: Büyük miktarda veri istemek ve aktarmak yavaş olabilir. Sayfalama, her isteğin yük boyutunu azaltır, bu da daha hızlı yanıt sürelerine ve azaltılmış sunucu yüküne yol açar.
  2. Kaynak Tüketimi: Daha küçük yanıtlar, onları oluşturan sunucuda ve onları ayrıştıran istemcide daha az bellek tüketir. Bu, özellikle mobil istemciler veya sınırlı kaynaklara sahip ortamlar için kritiktir.
  3. Hız Sınırlaması ve Kotalar: Birçok API hız sınırları uygular. Sayfalama, istemcilerin verileri bir kerede almaya çalışmak yerine, zaman içinde daha küçük parçalar halinde getirerek bu sınırlamalar dahilinde kalmasına yardımcı olur.
  4. Kullanıcı Deneyimi: API'yi tüketen kullanıcı arayüzleri için, verileri sayfalarda sunmak, kullanıcıları çok büyük bir liste veya çok uzun bir kaydırma ile bunaltmaktan çok daha kullanıcı dostudur.
  5. Veritabanı Verimliliği: Verilerin bir alt kümesini getirmek, özellikle uygun dizinleme yapılmışsa, genellikle tüm bir tabloyu almakla karşılaştırıldığında veritabanı üzerinde daha az yük oluşturur.

Yaygın Sayfalama Stratejileri

Sayfalamayı uygulamak için çeşitli yaygın stratejiler vardır ve her birinin kendine özgü avantaj ve dezavantajları vardır. En popüler olanları keşfedeceğiz: ofset/limit (genellikle sayfa tabanlı olarak anılır) ve imleç tabanlı (keyset veya arama sayfalama olarak da bilinir).

1. Ofset/Limit (veya Sayfa Tabanlı) Sayfalama

Bu, tartışmasız en basit ve en yaygın olarak benimsenen sayfalama yöntemidir. İstemcinin iki ana parametre belirtmesine izin vererek çalışır:

Alternatif olarak, istemciler şunları belirtebilir:

offset, page ve pageSize'den şu formülle hesaplanabilir: offset = (page - 1) * pageSize.

Teknik Uygulama Adımları:

/items adlı bir API uç noktamız olduğunu ve bu uç noktanın bir öğe listesi döndürdüğünü varsayalım.

a. API İstek Parametreleri:
İstemci şu şekilde bir istekte bulunur:
GET /items?offset=20&limit=10 (ilk 20'yi atlayarak 10 öğe getir)
veya
GET /items?page=3&pageSize=10 (sayfa başına 10 öğe ile 3. sayfayı getir, bu da offset=20, limit=10'a eşdeğerdir).

İstemci bunları sağlamazsa, bu parametreler için varsayılan değerler ayarlamak iyi bir uygulamadır (örneğin, limit=20, offset=0 veya page=1, pageSize=20). Ayrıca, istemcilerin aşırı sayıda kayıt talep etmesini önlemek için maksimum bir limit veya pageSize uygulayın; bu, sunucuyu zorlayabilir.

b. Arka Uç Mantığı (Kavramsal):
Sunucu bu isteği aldığında, bu parametreleri bir veritabanı sorgusuna çevirmesi gerekir.

// Spring Boot ile Java'da örnek
@GetMapping("/items")
public ResponseEntity<PaginatedResponse<Item>> getItems(
    @RequestParam(defaultValue = "0") int offset,
    @RequestParam(defaultValue = "20") int limit
) {
    // Kötüye kullanımı önlemek için limiti doğrulayın
    if (limit > 100) {
        limit = 100; // Maksimum limiti uygula
    }

    List<Item> items = itemRepository.findItemsWithOffsetLimit(offset, limit);
    long totalItems = itemRepository.countTotalItems(); // Meta veri için

    // Sayfalı yanıtı oluşturun ve döndürün
    // ...
}

c. Veritabanı Sorgusu (SQL Örneği):
Çoğu ilişkisel veritabanı, ofset ve limit ifadelerini doğrudan destekler.

PostgreSQL veya MySQL için:

SELECT *
FROM items
ORDER BY created_at DESC -- Tutarlı sıralama, kararlı sayfalama için çok önemlidir
LIMIT 10 -- Bu 'limit' parametresidir
OFFSET 20; -- Bu 'offset' parametresidir

SQL Server için (eski sürümler ROW_NUMBER() kullanabilir):

SELECT *
FROM items
ORDER BY created_at DESC
OFFSET 20 ROWS
FETCH NEXT 10 ROWS ONLY;

Oracle için:

SELECT *
FROM (
    SELECT i.*, ROWNUM rnum
    FROM (
        SELECT *
        FROM items
        ORDER BY created_at DESC
    ) i
    WHERE ROWNUM <= 20 + 10 -- offset + limit
)
WHERE rnum > 20; -- offset

Sıralama Üzerine Önemli Not: Ofset/limit sayfalama yönteminin güvenilir olması için, temel veri kümesi, tutarlı ve benzersiz (veya neredeyse benzersiz) bir anahtara veya anahtarların bir kombinasyonuna göre sıralanmalıdır. Öğelerin sırası istekler arasında değişebiliyorsa (örneğin, yeni öğeler ekleniyorsa veya öğeler sıralama düzenini etkileyecek şekilde güncelleniyorsa), kullanıcılar sayfaları gezinirken yinelenen öğeler görebilir veya öğeleri kaçırabilir. Yaygın bir seçim, oluşturma zaman damgası veya birincil kimliğe göre sıralamaktır.

d. API Yanıt Yapısı:
İyi bir sayfalama yanıtı, yalnızca geçerli sayfanın verilerini değil, aynı zamanda istemcinin gezinmesine yardımcı olacak meta verileri de içermelidir.

{
  "data": [
    // geçerli sayfa için öğe dizisi
    { "id": "item_21", "name": "Item 21", ... },
    { "id": "item_22", "name": "Item 22", ... },
    // ... 'limit' öğesine kadar
    { "id": "item_30", "name": "Item 30", ... }
  ],
  "pagination": {
    "offset": 20,
    "limit": 10,
    "totalItems": 5000, // Mevcut toplam öğe sayısı
    "totalPages": 500, // ceil(totalItems / limit) olarak hesaplanır
    "currentPage": 3 // (offset / limit) + 1 olarak hesaplanır
  },
  "links": { // Gezinme için HATEOAS bağlantıları
    "self": "/items?offset=20&limit=10",
    "first": "/items?offset=0&limit=10",
    "prev": "/items?offset=10&limit=10", // İlk sayfadaysa Null
    "next": "/items?offset=30&limit=10", // Son sayfadaysa Null
    "last": "/items?offset=4990&limit=10"
  }
}

HATEOAS (Uygulama Durumunun Motoru Olarak Hipermedya) bağlantıları (self, first, prev, next, last) sağlamak, bir REST en iyi uygulamasıdır. İstemcilerin URL'leri kendilerinin oluşturmak zorunda kalmadan sayfalar arasında gezinmelerine olanak tanır.

Ofset/Limit Sayfalamanın Artıları:

Ofset/Limit Sayfalamanın Eksileri:

2. İmleç Tabanlı (Keyset/Arama) Sayfalaması

İmleç tabanlı sayfalama, ofset/limit'in bazı eksikliklerini, özellikle büyük veri kümeleriyle performansı ve veri tutarlılığı sorunlarını ele alır. Mutlak bir ofsete güvenmek yerine, veri kümesindeki belirli bir öğeye işaret eden bir "imleç" kullanır. Daha sonra istemci, bu imlecin "sonrasındaki" veya "öncesindeki" öğeleri ister.

İmleç tipik olarak, önceki sayfada alınan son öğenin sıralama anahtar(lar)ının değer(ler)ini kodlayan opak bir dizedir.

Teknik Uygulama Adımları:

a. API İstek Parametreleri:
İstemci şu şekilde bir istekte bulunur:
GET /items?limit=10 (ilk sayfa için)
Ve sonraki sayfalar için:
GET /items?limit=10&after_cursor=opaquestringrepresentinglastitemid
Veya, geriye doğru sayfalama yapmak için (daha az yaygın ancak mümkün):
GET /items?limit=10&before_cursor=opaquestringrepresentingfirstitemid

limit parametresi hala sayfa boyutunu tanımlar.

b. İmleç Nedir?
Bir imleç şunlar olmalıdır:

c. Arka Uç Mantığı (Kavramsal):

// Spring Boot ile Java'da örnek
@GetMapping("/items")
public ResponseEntity<CursorPaginatedResponse<Item>> getItems(
    @RequestParam(defaultValue = "20") int limit,
    @RequestParam(required = false) String afterCursor
) {
    // Limiti doğrulayın
    if (limit > 100) {
        limit = 100;
    }

    // İmlecin kodunu çözerek son görülen öğenin özelliklerini alın
    // örneğin, LastSeenItemDetails lastSeen = decodeCursor(afterCursor);
    // afterCursor null ise, bu ilk sayfadır.

    List<Item> items;
    if (afterCursor != null) {
        DecodedCursor decoded = decodeCursor(afterCursor); // örneğin, { lastId: "some_uuid", lastCreatedAt: "timestamp" }
        items = itemRepository.findItemsAfter(decoded.getLastCreatedAt(), decoded.getLastId(), limit);
    } else {
        items = itemRepository.findFirstPage(limit);
    }

    String nextCursor = null;
    if (!items.isEmpty() && items.size() == limit) {
        // Öğelerin sıralandığı varsayılarak, sonraki imleci oluşturmak için listedeki son öğe kullanılır
        Item lastItemOnPage = items.get(items.size() - 1);
        nextCursor = encodeCursor(lastItemOnPage.getCreatedAt(), lastItemOnPage.getId());
    }

    // İmleç sayfalı yanıtı oluşturun ve döndürün
    // ...
}

// İmleçleri kodlamak/kodunu çözmek için yardımcı yöntemler
// private DecodedCursor decodeCursor(String cursor) { ... }
// private String encodeCursor(Timestamp createdAt, String id) { ... }

d. Veritabanı Sorgusu (SQL Örneği):
Anahtar, imleçten sıralama anahtarlarına göre kayıtları filtreleyen bir WHERE yan tümcesi kullanmaktır. ORDER BY yan tümcesi, imlecin bileşimiyle uyumlu olmalıdır.

created_at'e (azalan) ve ardından id'ye (azalan) göre sıralama yapıldığını varsayarsak, created_at benzersiz değilse kararlı sıralama için bir bağ kırıcı olarak:

İlk sayfa için:

SELECT *
FROM items
ORDER BY created_at DESC, id DESC
LIMIT 10;

Sonraki sayfalar için, imleç last_created_at_from_cursor ve last_id_from_cursor olarak kodunu çözdüyse:

SELECT *
FROM items
WHERE (created_at, id) < (CAST('last_created_at_from_cursor' AS TIMESTAMP), CAST('last_id_from_cursor' AS UUID)) -- Veya uygun türler
-- Artan sıralama için, > olurdu
-- Demet karşılaştırması (created_at, id) < (val1, val2) yazmanın öz bir yoludur:
-- WHERE created_at < 'last_created_at_from_cursor'
--    OR (created_at = 'last_created_at_from_cursor' AND id < 'last_id_from_cursor')
ORDER BY created_at DESC, id DESC
LIMIT 10;

Bu tür bir sorgu, özellikle (created_at, id) üzerinde bir dizin varsa çok verimlidir. Veritabanı, alakasız satırları taramadan doğrudan başlangıç noktasına "arama" yapabilir.

e. API Yanıt Yapısı:

{
  "data": [
    // geçerli sayfa için öğe dizisi
    { "id": "item_N", "createdAt": "2023-10-27T10:05:00Z", ... },
    // ... 'limit' öğesine kadar
    { "id": "item_M", "createdAt": "2023-10-27T10:00:00Z", ... }
  ],
  "pagination": {
    "limit": 10,
    "hasNextPage": true, // daha fazla veri olup olmadığını gösteren boole değeri
    "nextCursor": "base64encodedcursorstringforitem_M" // opak dize
    // İki yönlü imleçler destekleniyorsa, potansiyel olarak bir "prevCursor"
  },
  "links": {
    "self": "/items?limit=10&after_cursor=current_request_cursor_if_any",
    "next": "/items?limit=10&after_cursor=base64encodedcursorstringforitem_M" // Sonraki sayfa yoksa Null
  }
}

İmleç tabanlı sayfalama, genellikle totalPages veya totalItems sağlamaz, çünkü bunları hesaplamak tam bir tablo taraması gerektirir ve bu da bazı performans avantajlarını geçersiz kılar. Bunlara kesin olarak ihtiyaç duyuluyorsa, ayrı bir uç nokta veya bir tahmin sağlanabilir.

İmleç Tabanlı Sayfalamanın Artıları:

İmleç Tabanlı Sayfalamanın Eksileri:

Doğru Stratejiyi Seçmek

Ofset/limit ve imleç tabanlı sayfalama arasındaki seçim, özel gereksinimlerinize bağlıdır:

Bazı sistemlerde, hibrit bir yaklaşım bile kullanılır veya farklı kullanım durumları veya uç noktalar için farklı stratejiler sunulur.

Sayfalamayı Uygulama İçin En İyi Uygulamalar

Seçilen stratejiden bağımsız olarak, bu en iyi uygulamalara uyun:

  1. Tutarlı Parametre Adlandırması: Sayfalama parametreleriniz için net ve tutarlı adlar kullanın (örneğin, limit, offset, page, pageSize, after_cursor, before_cursor). API'niz boyunca tek bir kurala uyun (örneğin, camelCase veya snake_case).
  2. Gezinme Bağlantıları Sağlayın (HATEOAS): Yanıt örneklerinde gösterildiği gibi, self, next, prev, first ve last (uygun olduğunda) için bağlantılar ekleyin. Bu, API'yi daha keşfedilebilir hale getirir ve istemciyi URL oluşturma mantığından ayırır.
  3. Varsayılan Değerler ve Maksimum Limitler:
  1. Net API Dokümantasyonu: Sayfalama stratejinizi kapsamlı bir şekilde belgeleyin:
  1. Tutarlı Sıralama: Temel verilerin her sayfalama isteği için tutarlı bir şekilde sıralandığından emin olun. Ofset/limit için, bu veri kaymasını önlemek için hayati öneme sahiptir. İmleç tabanlı için, sıralama düzeni imleçlerin nasıl oluşturulduğunu ve yorumlandığını belirler. Birincil sıralama sütununun yinelenen değerlere sahip olabilmesi durumunda benzersiz bir bağ kırıcı sütun (birincil kimlik gibi) kullanın.
  2. Kenar Durumlarını İşleyin:
  1. Toplam Sayı Hususları:
  1. Hata İşleme: Hatalar için uygun HTTP durum kodları döndürün (örneğin, kötü girdi için 400, veri getirme sırasında sunucu hataları için 500).
  2. Güvenlik: Doğrudan bir sayfalama mekanizması olmasa da, sayfalama yapılan verilerin yetkilendirme kurallarına uymasını sağlayın. Bir kullanıcı yalnızca görmesine izin verilen verilerde sayfalama yapabilmelidir.
  3. Önbelleğe Alma: Sayfalı yanıtlar genellikle önbelleğe alınabilir. Ofset tabanlı sayfalama için, GET /items?page=2&pageSize=10 oldukça önbelleğe alınabilir. İmleç tabanlı için, GET /items?limit=10&after_cursor=XYZ de önbelleğe alınabilir. Önbelleğe alma stratejinizin, sayfalama bağlantılarının nasıl oluşturulduğu ve tüketildiği ile iyi çalıştığından emin olun. Temel veriler sık sık değişirse, geçersiz kılma stratejilerinin dikkate alınması gerekir.

Gelişmiş Konular (Kısa Bahisler)

Sonuç

Sayfalamayı doğru bir şekilde uygulamak, ölçeklenebilir ve kullanıcı dostu REST API'leri oluşturmanın temelidir. Ofset/limit sayfalama ile başlamak daha basit olsa da, imleç tabanlı sayfalama, büyük, dinamik veri kümeleri için üstün performans ve tutarlılık sunar. Her stratejinin teknik detaylarını anlayarak, uygulamanızın ihtiyaçlarına en uygun olanı seçerek ve uygulama ve API tasarımı için en iyi uygulamaları izleyerek, API'nizin, ölçekten bağımsız olarak, verileri istemcilerinize verimli bir şekilde iletmesini sağlayabilirsiniz. API tüketicileri için sorunsuz bir deneyim sağlamak için her zaman net dokümantasyona ve sağlam hata işlemeye öncelik vermeyi unutmayın.


Explore more

Fathom-R1-14B: Hindistan'dan Gelişmiş Yapay Zeka Muhakeme Modeli

Fathom-R1-14B: Hindistan'dan Gelişmiş Yapay Zeka Muhakeme Modeli

Yapay zeka hızla gelişiyor. FractalAIResearch/Fathom-R1-14B, 14.8 milyar parametreyle matematik ve genel akıl yürütmede başarılı.

5 June 2025

Mistral Code: İşletmeler için En Özelleştirilebilir Yapay Zeka Destekli Kodlama Asistanı

Mistral Code: İşletmeler için En Özelleştirilebilir Yapay Zeka Destekli Kodlama Asistanı

Mistral Code'u keşfedin: Kurumsal kullanıma özel, en özelleştirilebilir yapay zeka destekli kodlama asistanı.

5 June 2025

Claude Code'un 2025'te Yapay Zeka Kodlamasını Nasıl Dönüştürdüğü

Claude Code'un 2025'te Yapay Zeka Kodlamasını Nasıl Dönüştürdüğü

Claude Code, 2025'te yapay zeka destekli kodlamayı nasıl devrimleştiriyor? Özelliklerini, kullanımını ve Windsurf kısıtlamalarından sonra neden popüler olduğunu öğrenin. Geliştiriciler için okunması gereken!

5 June 2025

API Tasarım-Öncelikli Yaklaşımı Apidog'da Uygulayın

API'leri oluşturmanın ve kullanmanın daha kolay yolunu keşfedin