Isaac.

web

Pagination Strategies

Implement efficient pagination for large datasets.

By Emem IsaacMarch 23, 20242 min read
#pagination#offset limit#cursor#performance
Share:

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

  1. Limit max size: Cap pageSize at 100
  2. Default reasonable: Start with 10-20 items
  3. Total count: Include for UI
  4. Sorting: Consistent ordering required
  5. 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.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours