Pagination Strategies
Implement efficient pagination for large datasets.
By EMEPublished: February 20, 2025
paginationoffset limitcursorperformance
A Simple Analogy
Pagination is like dividing a book into chapters. Instead of loading the whole book, users get one chapter at a time.
Why Pagination?
- Performance: Don't load all data
- Bandwidth: Reduce transfer size
- UX: Faster page loads
- Database: Less query overhead
- Scalability: Handle large datasets
Offset/Limit
public async Task<PagedResult<ProductDto>> GetProducts(int page, int pageSize = 10)
{
var skip = (page - 1) * pageSize;
var total = await _db.Products.CountAsync();
var items = await _db.Products
.OrderBy(p => p.Id)
.Skip(skip)
.Take(pageSize)
.ToListAsync();
return new PagedResult<ProductDto>
{
Items = items,
Page = page,
PageSize = pageSize,
Total = total,
TotalPages = (total + pageSize - 1) / pageSize
};
}
Cursor-Based Pagination
public async Task<CursorPage<ProductDto>> GetProductsAsync(string cursor = null, int limit = 10)
{
var query = _db.Products.AsQueryable();
if (!string.IsNullOrEmpty(cursor))
{
var cursorId = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(cursor)));
query = query.Where(p => p.Id > cursorId);
}
var items = await query
.OrderBy(p => p.Id)
.Take(limit + 1)
.ToListAsync();
var hasMore = items.Count > limit;
items = items.Take(limit).ToList();
var nextCursor = hasMore ? items.Last().Id : null;
return new CursorPage<ProductDto>
{
Items = items,
NextCursor = nextCursor != null
? Convert.ToBase64String(Encoding.UTF8.GetBytes(nextCursor.ToString()))
: null,
HasMore = hasMore
};
}
Request/Response
// Request
[HttpGet("products")]
public async Task<IActionResult> GetProducts(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10)
{
return Ok(await _service.GetProducts(page, pageSize));
}
// Response
{
"items": [ ... ],
"page": 1,
"pageSize": 10,
"total": 500,
"totalPages": 50
}
Best Practices
- Limit max size: Cap pageSize at 100
- Default reasonable: Start with 10-20 items
- Total count: Include for UI
- Sorting: Consistent ordering required
- Index: Add database indexes on sort columns
Related Concepts
- Infinite scroll
- Load more
- Lazy loading
- Virtual scrolling
Summary
Implement pagination with offset/limit for simple cases or cursor-based for high-performance APIs.