Wiaoj.Pagination.OpenApi 0.2.0-alpha.3

This is a prerelease version of Wiaoj.Pagination.OpenApi.
dotnet add package Wiaoj.Pagination.OpenApi --version 0.2.0-alpha.3
                    
NuGet\Install-Package Wiaoj.Pagination.OpenApi -Version 0.2.0-alpha.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Wiaoj.Pagination.OpenApi" Version="0.2.0-alpha.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Wiaoj.Pagination.OpenApi" Version="0.2.0-alpha.3" />
                    
Directory.Packages.props
<PackageReference Include="Wiaoj.Pagination.OpenApi" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Wiaoj.Pagination.OpenApi --version 0.2.0-alpha.3
                    
#r "nuget: Wiaoj.Pagination.OpenApi, 0.2.0-alpha.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Wiaoj.Pagination.OpenApi@0.2.0-alpha.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Wiaoj.Pagination.OpenApi&version=0.2.0-alpha.3&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Wiaoj.Pagination.OpenApi&version=0.2.0-alpha.3&prerelease
                    
Install as a Cake Tool

Wiaoj.Pagination.OpenApi

Makes pagination visible in generated OpenAPI documents — both what WithPagination() does to an endpoint, and the shape of what it returns.


Installation

dotnet add package Wiaoj.Pagination.OpenApi
builder.Services.AddOpenApi(options => options.AddWiaojPagination());

That is the whole setup. Endpoints without WithPagination() are left untouched, so it is safe to add once for the entire document.


The problem it solves

WithPagination() installs an endpoint filter. The filter writes RFC 8288 Link headers, computes an ETag, and answers 304 Not Modified when the client sends a matching If-None-Match — none of which appears anywhere in the handler's signature.

So a document generated without this package describes an endpoint that returns 200 and sets no headers. That is not the endpoint that exists. A client generated from it has no way to follow rel="next", because as far as the document is concerned there is no Link header to read.

Neither is the result type described. PagedResult<T> and CursorResult<T> serialise through hand-written converters, and their CLR properties are an EquatableArray<T> and a CursorToken rather than an array and a string — nothing a schema generator reading properties can make sense of. Its answer to a type it cannot read is an empty schema, and openapi-typescript renders that as:

CursorResultOfProductDto: unknown;

The responses are correct; only the document is silent about them. It surfaces one step out, in the generated client, and the rational move for whoever hits it is to go back to whichever paging style is described — which is the wrong reason to choose a pagination strategy.


What it adds

PagedResult<T> / CursorResult<T> schemas { items: T[], metadata }, with items referencing T's own schema
PageMetadata / CursorMetadata schemas shared components, referenced rather than inlined per endpoint
Link response header on every 2xx response, when link headers are enabled
ETag response header on every 2xx response, when ETag evaluation is enabled
304 response when ETag evaluation is enabled
Paging query parameters page / size, or cursor / limit / direction

A cursor is described as a nullable string, not as the struct it is. A caller sends back what it was given; the inside of a cursor is not part of the contract, and describing it would invite clients to read it.

The parameter set is chosen from the endpoint's declared response type rather than guessed: PagedResult<T> means offset paging, CursorResult<T> means keyset. An endpoint returning neither gets nothing documented — no parameters, no Link, no ETag, no 304 — because the pagination filter only acts on a result it recognises as a page, and a document claiming those headers would describe an endpoint that does not send them.

An endpoint whose response carries the page inside an envelope says so with WithPagination<TResponse>(response => response.Metadata). That declares its style, so it is documented as offset or keyset from the declaration rather than inferred.

A handler returning IResult has no response type to read. WithPagination(PaginationStyle.Cursor) states the style for it. The statement never overrides a response type the document does know: a Produces<PagedResult<T>>() on an endpoint declared Cursor fails document generation instead of producing a document that describes parameters the endpoint does not accept.

Whether Link and ETag are documented follows the settings actually in effect — services.AddPagination(...), refined by the endpoint's own WithPagination(options => ...). The document resolves them through the same method, on the same metadata, the filter uses at run time, so it cannot advertise an ETag the application turned off.

Parameters the document already carries are left alone. A handler taking [AsParameters] PageRequest already has page and size described by ASP.NET Core, and adding them twice produces an invalid document. A handler taking CursorParameters has nothing described — a type that binds itself contributes no parameters to the document — so this is where cursor, limit and direction come from.

A handler taking a bare CursorRequest or PageRequest — without [AsParameters] — is a different endpoint: it binds the whole request from one query value, in the compact format cursor:limit:direction or page:size. The document is given that grammar, and the separate parameters are not added, because that endpoint does not accept them.

Defaults and bounds come from the types themselves — CursorRequest.DefaultLimit, CursorRequest.MaxLimit, PageRequest.DefaultSize, PageRequest.MaxSize — so the document cannot drift from the values the code actually enforces.


License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0-alpha.3 47 9/24/2026
0.2.0-alpha.2 45 9/24/2026
0.2.0-alpha.1 47 9/24/2026
0.1.0-alpha.9 49 9/21/2026
0.1.0-alpha.8 48 9/21/2026
0.1.0-alpha.7 48 9/18/2026
0.1.0-alpha.6 46 9/16/2026
0.1.0-alpha.5 51 9/16/2026
0.1.0-alpha.4 50 9/16/2026
0.1.0-alpha.3 48 9/15/2026
0.1.0-alpha.2 51 9/15/2026
0.1.0-alpha.1 70 9/14/2026
0.0.1-alpha.112-preview 50 9/13/2026
0.0.1-alpha.111-preview 49 9/13/2026
0.0.1-alpha.110-preview 53 9/12/2026
0.0.1-alpha.109-preview 75 9/11/2026
0.0.1-alpha.108-preview 73 9/8/2026
0.0.1-alpha.107-preview 70 9/8/2026
0.0.1-alpha.106-preview 63 9/8/2026
0.0.1-alpha.105-preview 60 9/8/2026
Loading failed