FoundationKit provides a small set of base classes and query models that standardise how data flows into and out of your API. Rather than inventing a new shape for every endpoint, every create operation starts fromDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Orbis25/FoundationKit/llms.txt
Use this file to discover all available pages before exploring further.
BaseInput, every update from BaseEdit, and every read response from BaseOutput. List endpoints are paginated uniformly using the Paginate query model and always return a PaginationResult<T> wrapper. This consistency means your controllers, repositories, and clients all speak the same language, and cross-cutting concerns like hiding internal audit fields from callers are handled once in the base classes rather than repeated across every DTO.
Input DTOs
Input DTOs represent the data a caller sends to your API when creating or updating a resource.BaseInput
BaseInput is an intentionally empty base class. Its sole purpose is to provide a common type constraint for the generic repositories — IMapRepository requires TInputModel : BaseInput. Extend it with whatever properties your create operation needs.
BaseInput to define your create payload:
BaseEdit
BaseEdit is the base class for update DTOs. It carries all the fields that the repository needs to perform an update — Id, UpdateAt, UpdatedBy, CreatedAt, and CreatedBy — but every one of them is decorated with [JsonIgnore]. This means the fields are present on the C# object and can be populated internally (the repository copies CreatedAt and CreatedBy from the existing database record before saving), but they are invisible to JSON deserialization. A caller cannot override audit or timestamp fields by injecting them into the request body.
BaseEdit to define your update payload. Add only the fields you want callers to be able to change:
Id on the edit model from the route parameter before calling Update, and it reads back CreatedAt / CreatedBy from the existing entity so those audit values are never lost on an update.
The
[JsonIgnore] attributes on BaseEdit are a deliberate security boundary. Even if a client sends "id": "..." or "createdBy": "..." in the JSON body, those values will be ignored during deserialization. The Id is always taken from the URL route, and CreatedAt/CreatedBy are always preserved from the database record.Output DTOs
Output DTOs represent the data your API returns to callers.BaseOutput
BaseOutput exposes the fields that are universally useful in a read response: the primary key, the formatted and raw creation timestamp, and the creator identifier. All downstream DTOs inherit these fields automatically.
BaseOutput to add your domain-specific response fields:
PersonDto response looks like:
Complete example: Person DTOs
The following is the full set of DTO classes from the FoundationKit example project, showing how all three layers fit together for aPerson resource.
Paginate
Paginate is the query model that callers send when requesting a paginated list. Bind it with [FromQuery] so ASP.NET Core maps the URL query string automatically.
Paginate properties
The 1-based page number to retrieve. Defaults to
1 if not supplied.The number of results to return per page (quantity). Defaults to
10.When
true, results are sorted in descending order. When false (the default), results are sorted ascending. Ordering defaults to CreatedAt unless overridden in the repository call.When
true, pagination is skipped and all matching records are returned in a single PaginationResult with ActualPage, Qyt, PageTotal, and Total all at their default values. This behaviour is implemented by BaseRepository.GetPaginatedListAsync and the QueryableExtensions.PaginateAsync extension method. MapRepository.GetPaginatedList does not implement NoPaginate handling and always paginates.A free-text search term. FoundationKit passes this value through to
PaginationResult.Query unchanged — you are responsible for applying it as a filter expression when you call GetPaginatedList or GetPaginatedListAsync.Using Paginate in a controller
PaginationResult<T>
PaginationResult<T> is the standard response envelope for all paginated list endpoints. The repository methods return it already populated — you just return it from your controller.
PaginationResult properties
The page number that was returned. Mirrors the
Page value from the incoming Paginate query.The page size that was used. Mirrors the
Qyt value from the incoming Paginate query.The total number of pages available, calculated as
ceil(Total / Qyt).The total number of records that match the filter, across all pages.
Echo of the search term from the
Paginate request, or null if none was supplied.The records for the current page.
null when no records match or when the repository has not populated the field.Example JSON response
IQueryable extension: QueryableExtensions
TheFoundationkit.Extensions.Enumerations.QueryableExtensions class provides two extension methods that let you apply the same Paginate → PaginationResult<T> pattern to any IQueryable<T>, regardless of whether you are using a FoundationKit repository or building a query from scratch.
PaginateAsync
Asynchronously materialises the query into aPaginationResult<TSource?>. When NoPaginate is true, all results are returned without slicing and ActualPage, Qyt, PageTotal, and Total are left at their default values.
Paginate (synchronous)
The synchronous overload works identically but blocks. Use it only when you are certain you are not in an async context (rare in ASP.NET Core).Both extension methods use the same
Page, Qyt, and NoPaginate fields from the Paginate object. They do not apply the Query search term — filtering must be applied to the IQueryable before calling PaginateAsync or Paginate, as shown in the example above.