Introduction
When building real-world applications using ASP.NET Core Web API, handling large amounts of data efficiently becomes very important. Imagine loading thousands of records in a single API response β it slows down performance, increases memory usage, and creates a poor user experience.
This is where Pagination in ASP.NET Core Web API comes into play.
Pagination helps you divide large datasets into smaller, manageable chunks (pages), making your API faster, scalable, and user-friendly.
What is Pagination?
Pagination is a technique used to split large data into smaller parts (pages).
Instead of returning all records at once, the API returns only a subset of data based on:
π Example:
If you have 1000 products:
This improves:
API performance
Response time
User experience
Why Pagination is Important in Web APIs
Letβs understand why pagination is crucial in ASP.NET Core Web API:
1. Improves Performance
Fetching only required records reduces database load and improves speed.
2. Reduces Memory Usage
Large datasets can consume high memory. Pagination keeps responses lightweight.
3. Better User Experience
Users can easily navigate through pages instead of scrolling endlessly.
4. Scalable APIs
Pagination ensures your API works efficiently even when data grows.
Types of Pagination
There are mainly three types of pagination:
1. Offset-Based Pagination
Uses Skip and Take.
Example:
Skip 10 records
Take next 10 records
2. Page Number-Based Pagination
Uses PageNumber and PageSize.
Example:
PageNumber = 2
PageSize = 10
3. Cursor-Based Pagination (Advanced)
Uses a pointer (cursor) instead of page numbers.
Best for large and real-time datasets.
Step-by-Step: Implement Pagination in ASP.NET Core Web API
Letβs implement pagination using a simple example.
Step 1: Create a Model
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Step 2: Create Pagination Parameters Class
public class PaginationParams
{
public int PageNumber { get; set; } = 1;
public int PageSize { get; set; } = 10;
private const int MaxPageSize = 50;
public void Validate()
{
if (PageNumber <= 0) PageNumber = 1;
if (PageSize <= 0) PageSize = 10;
if (PageSize > MaxPageSize) PageSize = MaxPageSize;
}
}
π This ensures:
Step 3: Modify Repository or Service Layer
public async Task<IEnumerable<Product>> GetProductsAsync(PaginationParams paginationParams)
{
paginationParams.Validate();
return await _context.Products
.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize)
.Take(paginationParams.PageSize)
.ToListAsync();
}
π Key logic:
Step 4: Update Controller
[HttpGet]
public async Task<IActionResult> GetProducts([FromQuery] PaginationParams paginationParams)
{
var products = await _productService.GetProductsAsync(paginationParams);
return Ok(products);
}
π Now API supports query parameters like:
/api/products?pageNumber=1&pageSize=10
Returning Pagination Metadata
Itβs a good practice to send pagination details along with data.
Create Response Wrapper
public class PagedResponse<T>
{
public IEnumerable<T> Data { get; set; }
public int TotalRecords { get; set; }
public int PageNumber { get; set; }
public int PageSize { get; set; }
}
Update Service
public async Task<PagedResponse<Product>> GetProductsAsync(PaginationParams paginationParams)
{
paginationParams.Validate();
var query = _context.Products.AsQueryable();
var totalRecords = await query.CountAsync();
var data = await query
.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize)
.Take(paginationParams.PageSize)
.ToListAsync();
return new PagedResponse<Product>
{
Data = data,
TotalRecords = totalRecords,
PageNumber = paginationParams.PageNumber,
PageSize = paginationParams.PageSize
};
}
Controller Response
return Ok(await _productService.GetProductsAsync(paginationParams));
π Now response includes:
Total records
Current page
Page size
Advanced Pagination with Filtering and Sorting
Pagination is often used with filtering and sorting.
Example:
public async Task<IEnumerable<Product>> GetProductsAsync(string search, string sortBy, PaginationParams paginationParams)
{
var query = _context.Products.AsQueryable();
if (!string.IsNullOrEmpty(search))
{
query = query.Where(p => p.Name.Contains(search));
}
if (!string.IsNullOrEmpty(sortBy))
{
query = sortBy.ToLower() switch
{
"price" => query.OrderBy(p => p.Price),
_ => query.OrderBy(p => p.Name)
};
}
return await query
.Skip((paginationParams.PageNumber - 1) * paginationParams.PageSize)
.Take(paginationParams.PageSize)
.ToListAsync();
}
π This makes your API powerful and flexible.
Best Practices for Pagination in ASP.NET Core
1. Always Set Maximum Page Size
Prevents heavy queries that can crash your API.
2. Use Async Methods
Improves performance and scalability.
3. Return Metadata
Helps frontend understand total pages.
4. Use Indexing in Database
Improves query performance.
5. Consider Cursor Pagination for Large Data
Better for real-time systems like feeds.
Common Mistakes to Avoid
β Returning all data without pagination
β Not validating PageSize
β Ignoring total count
β Using large page sizes
Real-World Example Use Case
Imagine an e-commerce application:
Products list β paginated
Orders history β paginated
Search results β paginated
Without pagination, your API will become slow and inefficient.
Summary
Pagination in ASP.NET Core Web API is a must-have feature for building scalable and high-performance applications. By using techniques like Skip and Take, along with proper validation and metadata, you can create APIs that are fast, efficient, and user-friendly. Implementing pagination not only improves backend performance but also enhances the overall user experience by delivering data in a structured and manageable way.