Skip to content

Commit 473e624

Browse files
committed
feat: externalize pagination settings to configurable options and apply them across endpoints
1 parent f920031 commit 473e624

8 files changed

Lines changed: 63 additions & 14 deletions

File tree

src/ApiService/BookStore.ApiService/Endpoints/AuthorEndpoints.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Marten.Pagination;
55
using Microsoft.AspNetCore.Http.HttpResults;
66
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.Extensions.Options;
78

89
namespace BookStore.ApiService.Endpoints;
910

@@ -26,9 +27,10 @@ public static RouteGroupBuilder MapAuthorEndpoints(this RouteGroupBuilder group)
2627

2728
static async Task<Ok<IPagedList<AuthorProjection>>> GetAuthors(
2829
[FromServices] IQuerySession session,
30+
[FromServices] IOptions<PaginationOptions> paginationOptions,
2931
[AsParameters] PagedRequest request)
3032
{
31-
var paging = request.Normalize();
33+
var paging = request.Normalize(paginationOptions.Value);
3234

3335
// Use Marten's native pagination for optimal performance
3436
var pagedList = await session.Query<AuthorProjection>()

src/ApiService/BookStore.ApiService/Endpoints/BookEndpoints.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Marten.Pagination;
55
using Microsoft.AspNetCore.Http.HttpResults;
66
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.Extensions.Options;
78
using Npgsql;
89

910
namespace BookStore.ApiService.Endpoints;
@@ -29,10 +30,11 @@ public static RouteGroupBuilder MapBookEndpoints(this RouteGroupBuilder group)
2930

3031
static async Task<Ok<PagedListDto<BookSearchProjection>>> SearchBooks(
3132
[FromServices] IQuerySession session,
33+
[FromServices] IOptions<PaginationOptions> paginationOptions,
3234
[AsParameters] PagedRequest request,
3335
[FromQuery] string? search = null)
3436
{
35-
var paging = request.Normalize();
37+
var paging = request.Normalize(paginationOptions.Value);
3638

3739
if (string.IsNullOrWhiteSpace(search))
3840
{

src/ApiService/BookStore.ApiService/Endpoints/CategoryEndpoints.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Marten.Pagination;
66
using Microsoft.AspNetCore.Http.HttpResults;
77
using Microsoft.AspNetCore.Mvc;
8+
using Microsoft.Extensions.Options;
89

910
namespace BookStore.ApiService.Endpoints;
1011

@@ -29,10 +30,11 @@ public static RouteGroupBuilder MapCategoryEndpoints(this RouteGroupBuilder grou
2930

3031
static async Task<IResult> GetCategories(
3132
[FromServices] IQuerySession session,
33+
[FromServices] IOptions<PaginationOptions> paginationOptions,
3234
[AsParameters] PagedRequest request,
3335
HttpContext context)
3436
{
35-
var paging = request.Normalize();
37+
var paging = request.Normalize(paginationOptions.Value);
3638
var language = GetPreferredLanguage(context);
3739

3840
// Use Marten's native pagination for optimal performance

src/ApiService/BookStore.ApiService/Endpoints/PublisherEndpoints.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Marten.Pagination;
55
using Microsoft.AspNetCore.Http.HttpResults;
66
using Microsoft.AspNetCore.Mvc;
7+
using Microsoft.Extensions.Options;
78

89
namespace BookStore.ApiService.Endpoints;
910

@@ -26,9 +27,10 @@ public static RouteGroupBuilder MapPublisherEndpoints(this RouteGroupBuilder gro
2627

2728
static async Task<Ok<IPagedList<PublisherProjection>>> GetPublishers(
2829
[FromServices] IQuerySession session,
30+
[FromServices] IOptions<PaginationOptions> paginationOptions,
2931
[AsParameters] PagedRequest request)
3032
{
31-
var paging = request.Normalize();
33+
var paging = request.Normalize(paginationOptions.Value);
3234

3335
// Use Marten's native pagination for optimal performance
3436
var pagedList = await session.Query<PublisherProjection>()

src/ApiService/BookStore.ApiService/Models/Pagination.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ namespace BookStore.ApiService.Models;
66
public record PagedRequest
77
{
88
/// <summary>
9-
/// Maximum number of items allowed per page
9+
/// Default page number (1-based)
1010
/// </summary>
11-
public const int MaxPageSize = 100;
11+
public const int DefaultPage = 1;
1212

1313
/// <summary>
1414
/// Page number (1-based)
@@ -21,18 +21,19 @@ public record PagedRequest
2121
public int? PageSize { get; init; }
2222

2323
/// <summary>
24-
/// Validates and normalizes pagination parameters
24+
/// Validates and normalizes pagination parameters using configuration options
2525
/// </summary>
26-
public PagedRequest Normalize()
26+
public PagedRequest Normalize(PaginationOptions options)
2727
{
28-
var page = int.Max(1, Page ?? 1);
29-
var pageSize = int.Clamp(PageSize ?? 20, 1, MaxPageSize);
28+
var page = int.Max(DefaultPage, Page ?? DefaultPage);
29+
var pageSize = int.Clamp(PageSize ?? options.DefaultPageSize, 1, options.MaxPageSize);
3030

3131
return this with { Page = page, PageSize = pageSize };
3232
}
3333

3434
/// <summary>
35-
/// Calculates the number of items to skip
35+
/// Calculates the number of items to skip using configuration options
3636
/// </summary>
37-
public int Skip => ((Page ?? 1) - 1) * (PageSize ?? 20);
37+
public int GetSkip(PaginationOptions options) =>
38+
((Page ?? DefaultPage) - 1) * (PageSize ?? options.DefaultPageSize);
3839
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace BookStore.ApiService.Models;
2+
3+
/// <summary>
4+
/// Configuration options for pagination
5+
/// </summary>
6+
public sealed class PaginationOptions
7+
{
8+
/// <summary>
9+
/// Configuration section name
10+
/// </summary>
11+
public const string SectionName = "Pagination";
12+
13+
/// <summary>
14+
/// Default value for page size when not specified
15+
/// </summary>
16+
public const int DefaultPageSizeValue = 20;
17+
18+
/// <summary>
19+
/// Default value for maximum number of items allowed per page
20+
/// </summary>
21+
public const int MaxPageSizeValue = 100;
22+
23+
/// <summary>
24+
/// Default page size when not specified
25+
/// </summary>
26+
public int DefaultPageSize { get; init; } = DefaultPageSizeValue;
27+
28+
/// <summary>
29+
/// Maximum number of items allowed per page
30+
/// </summary>
31+
public int MaxPageSize { get; init; } = MaxPageSizeValue;
32+
}

src/ApiService/BookStore.ApiService/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
// Add services to the container.
4343
builder.Services.AddProblemDetails();
4444

45+
// Configure pagination options
46+
builder.Services.Configure<BookStore.ApiService.Models.PaginationOptions>(
47+
builder.Configuration.GetSection(BookStore.ApiService.Models.PaginationOptions.SectionName));
48+
4549
// Configure OpenAPI with metadata
4650
builder.Services.AddOpenApi(options => options.AddBookStoreApiDocumentation());
4751

src/ApiService/BookStore.ApiService/appsettings.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@
2222
}
2323
}
2424
},
25-
"AllowedHosts": "*"
26-
}
25+
"AllowedHosts": "*",
26+
"Pagination": {
27+
"DefaultPageSize": 20,
28+
"MaxPageSize": 100
29+
}
30+
}

0 commit comments

Comments
 (0)