Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!
[NODE JS] Express ile Sayfalama (Pagination)
Veritabanınızda binlerce, hatta milyonlarca kayıt olduğunda hepsini tek seferde döndürmek pratik değildir. Hem sunucu kaynaklarını tüketir hem de istemci bu kadar veriyi işleyemez. Sayfalama ile veriyi küçük parçalara bölerek her seferinde sadece bir dilim döndürürsünüz. Bu hem performansı artırır hem de kullanıcı deneyimini iyileştirir.
İki temel sayfalama yöntemi var: offset-based ve cursor-based. Offset-based en yaygın olanıdır: sayfa ve limit parametreleriyle çalışır. ?sayfa=3&limit=20 gibi. Cursor-based ise son görülen kaydın ID'sini referans alır: ?after=abc123&limit=20 gibi. Büyük veri setlerinde cursor-based daha performanslı çünkü veritabanının offset kadar kayıt atlaması gerekmez.
Ben ilk API'lerimde sayfalama yapmadığım için ciddi performans sorunları yaşadım. 50.000 kayıtlık bir tabloyu tek seferde döndürmeye çalışıyordum ve hem sunucu hem de frontend donuyordu. Sayfalama ekledikten sonra response süresi saniyelerden milisaniyelere düştü.
Sayfalama response'unda metadata çok önemli. Toplam kayıt sayısı, mevcut sayfa, sayfa başına kayıt, toplam sayfa sayısı ve navigasyon linkleri (önceki, sonraki, ilk, son) bulunmalıdır. Bu bilgiler istemcinin sayfalama UI'ını oluşturmasını sağlar.
Varsayılan değerler ve limitler belirlemelisiniz. Sayfa parametresi verilmezse 1, limit verilmezse 20 gibi makul varsayılanlar kullanın. Limit'e üst sınır koyun, mesela maksimum 100. Aksi halde birisi ?limit=1000000 gönderip sunucunuzu zorlayabilir.
Offset-based sayfalamada tutarsızlık sorunu olabilir. Kullanıcı 2. sayfadayken yeni bir kayıt eklenirse veya bir kayıt silinirse sayfa içerikleri kayabilir. Cursor-based pagination bu sorunu çözer çünkü belirli bir noktadan sonraki kayıtları getirir, sıra numarasına bağlı değildir.
Sayfalama ve sıralama birlikte çalışmalıdır. Kullanıcı tarihe göre sıralayıp 3. sayfayı isteyebilir. Sıralama ve sayfalama birbirine bağımlıdır çünkü farklı sıralamada farklı kayıtlar farklı sayfalara düşer. Sıralama kriterini sayfalama ile birlikte veritabanı sorgusuna yansıtmanız gerekir.
Infinite scroll ve load more butonu frontend tarafında sayfalamayı kullanan iki yaygın pattern. Her ikisi de arkada aynı API'yi çağırır, sadece UI farklıdır. Backend açısından ikisi arasında fark yoktur.
JavaScript:
const express = require('express');
const app = express();
// 200 ornek kayit olustur
const urunler = Array.from({{ length: 200 }}, (_, i) => ({{
id: i + 1,
ad: `Urun ${{i + 1}}`,
fiyat: Math.floor(Math.random() * 10000) + 100,
kategori: ['elektronik', 'giyim', 'kitap', 'gida'][i % 4],
puan: (Math.random() * 5).toFixed(1),
olusturma: new Date(2024, 0, 1 + i).toISOString(),
}}));
// Offset-based pagination
app.get('/api/urunler', (req, res) => {{
let sonuc = [...urunler];
// Filtreleme
if (req.query.kategori) sonuc = sonuc.filter(u => u.kategori === req.query.kategori);
if (req.query.minFiyat) sonuc = sonuc.filter(u => u.fiyat >= Number(req.query.minFiyat));
if (req.query.maxFiyat) sonuc = sonuc.filter(u => u.fiyat <= Number(req.query.maxFiyat));
// Siralama
const sort = req.query.siralama || 'id';
const order = req.query.sira === 'desc' ? -1 : 1;
sonuc.sort((a, b) => (a[sort] > b[sort] ? order : -order));
// Sayfalama
const sayfa = Math.max(1, parseInt(req.query.sayfa) || 1);
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 20));
const toplam = sonuc.length;
const toplamSayfa = Math.ceil(toplam / limit);
const offset = (sayfa - 1) * limit;
sonuc = sonuc.slice(offset, offset + limit);
const baseUrl = `${{req.protocol}}://${{req.get('host')}}${{req.baseUrl}}${{req.path}}`;
const queryStr = (s) => `${{baseUrl}}?sayfa=${{s}}&limit=${{limit}}`;
res.json({{
veri: sonuc,
sayfalama: {{
toplam,
sayfa,
limit,
toplamSayfa,
oncekiSayfa: sayfa > 1 ? sayfa - 1 : null,
sonrakiSayfa: sayfa < toplamSayfa ? sayfa + 1 : null,
links: {{
self: queryStr(sayfa),
ilk: queryStr(1),
son: queryStr(toplamSayfa),
onceki: sayfa > 1 ? queryStr(sayfa - 1) : null,
sonraki: sayfa < toplamSayfa ? queryStr(sayfa + 1) : null,
}},
}},
}});
}});
// Cursor-based pagination
app.get('/api/urunler/cursor', (req, res) => {{
const limit = Math.min(50, parseInt(req.query.limit) || 20);
const afterId = parseInt(req.query.after) || 0;
let sonuc = urunler.filter(u => u.id > afterId);
const hasMore = sonuc.length > limit;
sonuc = sonuc.slice(0, limit);
const sonKayitId = sonuc.length > 0 ? sonuc[sonuc.length - 1].id : null;
res.json({{
veri: sonuc,
cursor: {{
after: sonKayitId,
hasMore,
count: sonuc.length,
nextUrl: hasMore ? `/api/urunler/cursor?after=${{sonKayitId}}&limit=${{limit}}` : null,
}},
}});
}});
app.listen(3000, () => console.log('Pagination sunucusu port 3000'));
Sayfalama her API'nin olmazsa olmazıdır. Küçük veri setlerinde gereksiz görünebilir ama veriler büyüdüğünde sayfalama olmadan uygulama kullanılamaz hale gelir. İlk günden doğru bir sayfalama altyapısı kurmak sizi ileride çok büyük refactoring'den kurtarır.
Emeğiniz ve katkınız için teşekkür ederiz!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!
Konunuz gerekli incelemelerden geçerek onaylandı. Topluluğumuza katkılarınızın devamını bekliyoruz.
İyi forumlar!

