A skeleton .NET API project using Clean Architecture. It exposes a filter-set to the caller—any field on any object can be filtered on—while keeping the codebase focused.
NetSkelly is a .NET API skeleton project designed with Clean Architecture principles to provide a robust starting point for web services. It addresses the common need for flexible data querying by implementing a dynamic, URL-based filtering mechanism that allows clients to filter any field on returned objects using various operators.
The project also features a comprehensive authentication and authorization system, including multi-factor authentication (MFA) and JWT-based security.
Its modular design separates concerns into API, Application, Domain, and Infrastructure layers, promoting maintainability and scalability.
- Custom Dynamic Query Filtering: Implemented a robust URL-based filtering system using a custom model binder and parser, enabling dynamic filtering on any object property with various operators, significantly reducing API endpoint boilerplate for data retrieval.
- Adherence to Clean Architecture: The project's explicit separation of concerns into API, Application, Domain, and Infrastructure projects demonstrates a commitment to modularity, testability, and long-term maintainability.
- Centralized API Exception Handling: Utilized an ASP.NET Core ExceptionFilter to convert application-layer exceptions into standardized HTTP responses, streamlining error management and ensuring a consistent API error contract.
generated by RepoLens
imagine an endpoint https://net.skelly/record and a some "Record" objects as follows:
[
{
"id": 1,
"name": "lorem recordum",
"is_cool": true,
"points": 12,
"created_at": 2026-04-11 15:00,
},
{
"id": 2,
"name": "dorum et nihilus",
"is_cool": false,
"points": 15,
"created_at": 2026-04-11 16:00,
},
{
"id": 2,
"name": "dali to pahealus",
"is_cool": true,
"points": 33,
"created_at": 2026-04-11 14:00,
},
]The API user can then fetch a specific record with https://net.skelly/record/{id} as expected, but they can also list
records using a set of filters which correspond to the properties on the objects returned.
This is most effectively done through the creation of a filters object on the calling code, so that the url will contain
?filters[foo]=bar. The obvious is an equals operation so it defaults to that. so for example if filtering for all things
cool then ?filters[is_cool]=true would return the 2 items listed as cool.
this is done on the root "uri" like https://net.skell/record?filters[is_cool]=true
It can also use the following operators:
prop.eq-> equalsprop.ne-> not equalsprop.lt-> less thanprop.lte-> less than or equal toprop.gt-> greater thanprop.gte-> greater than or equal toprop.contains-> string comparison, returns all that contain the query
multiple filters on the same property can also be passed which will be converted into a logical AND for lt, lte,
gt, and gte and a logical OR for eq, ne, and contains.
see the EFRepository class for more details on listing with filters.