One feature file per endpoint. Generated ASP.NET Core registration, checks, clients, and portability hints.
Website: https://sano-suguru.github.io/slicefx/
SliceFx is an experimental .NET framework built on ASP.NET Core Minimal APIs. A feature is one file: request, response, handler, validation, and filters together. A source generator turns it into standard Minimal API registrations, a route manifest, and typed clients, so nothing needs hand-syncing.
[Feature("POST /users", Summary = "Create a new user")]
public static class CreateUser
{
public record Request([Required, MinLength(2)] string Name, [Required, EmailAddress] string Email);
public record Response(Guid Id, string Name, string Email, DateTime CreatedAt);
public static async Task<Response> Handle(Request req, IUserStore store, CancellationToken ct)
{
var user = await store.AddAsync(req.Name, req.Email, ct);
return new Response(user.Id, user.Name, user.Email, user.CreatedAt);
}
}This is the whole feature: route, validation, and handler. The generated AddSlice() / MapSlices() calls wire it up.
Curious about the design choices? See Design decisions FAQ and Production readiness criteria.
| Need | SliceFx provides |
|---|---|
| Endpoint code that is easy to review | One feature file per endpoint: request, response, handler, validation, and filters stay together. |
| Less hand-synced API glue | AddSlice() / MapSlices(), route metadata, and typed clients are generated from the same feature definitions. |
| Standard ASP.NET Core behavior | Minimal API binding, DI, endpoint filters, DataAnnotations, OpenAPI compatibility, and IResult remain available. |
| Native AOT-friendly startup | Generated MapMethods calls avoid startup route scanning; SliceFx.Core has no PackageReference entries and only uses the Microsoft.AspNetCore.App framework reference. Add [assembly: SliceAspNetAot] to switch to reflection-free generated dispatch and publish a native binary with zero IL2026/IL3050 diagnostics. |
| Early portability feedback | slicefx routes classifies each endpoint as portable, partial, or aspnet-only; Lambda and wasi:http adapters are optional. |
| Low lock-in | Generated code compiles down to standard MapMethods calls. Remove the source generator reference and expand the output in place for a low-residue exit path. |
What SliceFx is not: a replacement for ASP.NET Core, or a mediator/pipeline framework like MediatR. Auth, rate limiting, CORS, and IEndpointFilter all still work as-is (see What you keep). SliceFx is a generated layer around Minimal APIs that compiles down to plain WebApplication.MapMethods calls, so dropping the source generator reference leaves the generated code working as your exit path. You don't need to rewrite anything: add it to one endpoint in an existing app and keep the rest on controllers or handwritten Minimal APIs (migrating from Minimal APIs, migrating from controllers).
dotnet run --project samples/SliceFx.Sample
curl http://localhost:5099/healthSliceFx is pre-1.0 experimental software. Preview packages use 0.x versions until the API is intentionally stabilized. A subset of SliceFx.Core — [Feature], [Filter<T>], ISliceValidator<T>, SliceValidationResult, SliceResult<T>/SliceResult — already carries an explicit stability commitment; see docs/api-stability-policy.md for scope and terms.
The latest preview is published on NuGet — see the NuGet badge above. Install from NuGet:
dotnet add package SliceFx.Core --prerelease
dotnet add package SliceFx.SourceGenerator --prereleaseSee the package table for satellite packages. NuGet package pages: SliceFx.Core, SliceFx.SourceGenerator, SliceFx.Cli.
SDK and analyzer policy: the repository targets .NET 10 with SDK 10.0.300 pinned in global.json and rollForward: latestFeature. Warnings and code-analysis diagnostics are treated as errors, but the analyzer recommendation set is pinned to 10.0-recommended so normal PR/main builds do not unexpectedly break when a newer SDK promotes analyzer rules. A separate analyzer canary workflow checks the latest .NET 10 analyzer behavior and reports drift for dedicated maintenance PRs.
WASI support (SliceFx.Wasi) is experimental and depends on an unstable upstream toolchain: componentize-dotnet, NativeAOT-LLVM preview packages, WASI Preview 2 / wasi:http@0.2, and Cloudflare's JS transpile/shim path when targeting Workers. Native WASI publish requires Linux x64 or Windows x64; macOS requires a Linux x64 Docker cross-build. "Experimental" means SliceFx.Wasi's own 0.x API may change; "unstable upstream toolchain" means those external build and deployment tools may break independently of SliceFx runtime code.
All adapters are opt-in. Reference only the packages you need; the source generator emits code only for the surfaces you reference. An ASP.NET-only app uses just SliceFx.Core and SliceFx.SourceGenerator.
| Package | Purpose |
|---|---|
SliceFx.Core |
Core runtime: [Feature], [Filter<T>], validation, and endpoint filters. |
SliceFx.SourceGenerator |
AOT-friendly generated registrations and route metadata. |
SliceFx.Lambda |
ASP.NET-hosted AWS Lambda adapter. |
SliceFx.Lambda.FunctionPerFeature |
Experimental HTTP API v2 function-per-feature Lambda handlers. |
SliceFx.TestHost |
In-process test host helpers. |
SliceFx.Wasi |
ASP.NET-independent wasi:http dispatch. |
SliceFx.Wasi.KeyValue |
IKeyValueStore abstraction and in-memory test double for WASI features. |
SliceFx.Wasi.HttpClient |
IWasiHttpClient abstraction and in-memory test double for outgoing HTTP in WASI features. |
SliceFx.Wasi.Spin |
ISpinCronHandler abstraction, SpinCronDispatcher, and recording test double for Spin cron trigger integration in WASI features. |
SliceFx.Cli |
Scaffolding, route inspection, AWS SAM manifest/package helpers, and typed client generation. |
Add SliceFx.Core and SliceFx.SourceGenerator at the same preview version:
dotnet add package SliceFx.Core --prerelease
dotnet add package SliceFx.SourceGenerator --prereleaseProgram.cs:
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddSlice();
builder.Services.AddSingleton<IUserStore, InMemoryUserStore>();
var app = builder.Build();
app.MapSlices();
app.Run();Features/Users/CreateUser.cs:
namespace SliceFx.Sample.Features.Users;
[Feature("POST /users", Summary = "Create a new user")]
public static class CreateUser
{
public record Request(
[Required, MinLength(2)] string Name,
[Required, EmailAddress] string Email);
public record Response(Guid Id, string Name, string Email, DateTime CreatedAt);
public static async Task<Response> Handle(Request req, IUserStore store, CancellationToken ct)
{
var user = await store.AddAsync(req.Name, req.Email, ct);
return new Response(user.Id, user.Name, user.Email, user.CreatedAt);
}
}The generator discovers [Feature] classes, emits AddSlice() / MapSlices(), wires Minimal API binding, and attaches validation. Prefer plain response records for portable endpoints. Use IResult when the feature intentionally needs ASP.NET-specific response helpers such as Results.NotFound() or Results.NoContent().
DI binding note: On the ASP.NET path binding is plain Minimal API — any registered service (concrete or interface) resolves from DI automatically, identical to raw Minimal API. Annotate concrete service params with
[FromServices](keyed services with[FromKeyedServices(key)]) only if you want the handler portable across ASP.NET, WASI, and Lambda — the portable-dispatch generator cannot probe the DI container at compile time. Seedocs/guides/parameter-binding.mdandsamples/SliceFx.Sample/Features/Users/PromoteUser.cs.
Feature filters are standard ASP.NET Core IEndpointFilter types:
[Feature("DELETE /users/{id:guid}", Summary = "Delete a user")]
[Filter<RequestLoggingFilter>]
[Filter<RequireApiKeyFilter>]
public static class DeleteUser
{
public static async Task<IResult> Handle(Guid id, IUserStore store, CancellationToken ct)
{
var user = await store.GetAsync(id, ct);
if (user is null) return Results.NotFound();
await store.RemoveAsync(id, ct);
return Results.NoContent();
}
}AddSlice() registers referenced filters and matching validators as scoped services, and [Filter<T>] applies filters in declaration order. Supported DataAnnotations rules are generated and run first; closed ISliceValidator<TRequest> implementations are discovered when TRequest is a Slice request parameter and run automatically before feature filters for rules that need code. A request type can have one Slice validator; orphan validators fail the build so validation is never skipped silently.
For production authorization policies, prefer ASP.NET Core Authorization. Slice filters are best for explicit per-feature endpoint filter behavior.
Read more:
SliceFx generated code is pure Minimal API expansion — the full ASP.NET surface stays available.
Features you keep on every endpoint:
- ASP.NET Core Authorization —
[Authorize], policies, fallback policy,RequireAuthorization() - Output caching —
CacheOutput()via route group - Rate limiting —
RequireRateLimiting()via route group - CORS —
RequireCors()via route group - Exception handling middleware and
IProblemDetailsService [Filter<T>]endpoint filters with fullIEndpointFilteraccess- OpenAPI integration —
builder.Services.AddOpenApi()/app.MapOpenApi() - Standard Minimal API binding —
[FromRoute],[FromQuery],[FromHeader],[FromForm],[FromServices],[FromKeyedServices]
Escape hatches for common needs:
| Need | Default path | Escape hatch |
|---|---|---|
| Validation | DataAnnotations attributes on the request record | Implement ISliceValidator<T> for the request type |
| Authorization | ASP.NET Core Authorization ([Authorize], policies) |
Group-level RequireAuthorization(...) or fallback policy |
| Rate limiting / caching / CORS | Route group: app.MapGroup("/api").RequireRateLimiting(...).MapSlices() |
Per-group or per-endpoint policy |
| Cross-cutting behavior | [Filter<T>] endpoint filter |
Standard ASP.NET Core middleware |
For more detail see ASP.NET features and escape hatches.
| Feature | Status |
|---|---|
[Feature("METHOD /path")] declarative routing |
Implemented |
Source-generated AddSlice() / MapSlices() |
Implemented |
Static handlers with body / route / query / DI / CancellationToken binding |
Implemented |
| Source-generated DataAnnotations validation for supported property/positional-record attributes | Implemented |
ISliceValidator<T> custom validation |
Implemented |
[Filter<T>] endpoint filters |
Implemented |
| Route metadata manifest | Experimental |
slicefx routes portability classification |
Experimental |
slicefx client csharp typed client generation |
Experimental |
slicefx client typescript typed fetch client generation |
Experimental |
| AWS SAM manifest generation | Experimental |
| ASP.NET-hosted Lambda adapter | Experimental |
| Function-per-feature Lambda handlers | Experimental HTTP API v2 NativeAOT binary-per-feature packaging |
| TestHost helper | Experimental |
| WASI adapter | Experimental single-component in-process wasi:http dispatch; per-feature WASM packaging is not implemented |
SliceResult<T> / SliceResult typed WASI results |
Implemented — host-neutral result structs in SliceFx.Core; source generator + CLI client generator unwrap to the payload type |
| Plain NativeAOT ASP.NET host | Experimental — [assembly: SliceAspNetAot] opts into generated AOT-safe dispatch; linux-x64 publish + smoke test CI-gated |
The C# typed client reuses C# request/response types rather than generating DTO copies. Use nested feature DTOs when the client can reference the feature assembly; use non-nested DTOs in a shared contracts project when Blazor or another .NET client should reference contracts without referencing server features. The TypeScript client emits interfaces from built metadata. Routes returning SliceResult<T> generate Task<T> methods; routes returning the non-generic SliceResult generate Task (void) methods.
Maintainer dogfooding is live: slicefx-inbox has been running on Fermyon Cloud (Spin WASI, wasi:http/incoming-handler) since preview.5. All 11 handlers return SliceResult<T> / SliceResult (preview.7+), with SliceApiClient.g.cs fully generated.
Most projects use all three portability classes in the same codebase — the classification tells tooling where a feature can run, not whether it is well-written.
The source generator classifies each feature endpoint at build time. slicefx routes reports the result; the same data drives typed-client generation, WASI route tables, and Lambda function-per-feature eligibility.
| Class | Meaning |
|---|---|
portable |
Returns a plain record or void. Eligible for typed-client generation, WASI dispatch, and function-per-feature Lambda. |
partial |
Portable handler shape, but attached endpoint filters are ASP.NET-only today. |
aspnet-only |
Returns IResult or uses ASP.NET-specific behavior. The full Minimal API feature set is available. |
aspnet-only features are standard Minimal API endpoints with the complete ASP.NET ecosystem available — they are not penalized or degraded.
Add [assembly: SliceAspNetAot] to any assembly to switch from the default RequestDelegateFactory registration path to generated AOT-safe handlers. The generator emits new RequestDelegate(…) methods that bind parameters, run validation, and serialize responses using JsonTypeInfo<T> — no per-request reflection. Publishing with PublishAot=true and TreatWarningsAsErrors=true then fails on any residual IL2026/IL3050 diagnostic.
// AotSetup.cs
[assembly: SliceAspNetAot]// AotJsonContext.cs — covers every request/response type the generator will serialize
[SliceJsonContext(SliceJsonTarget.AspNet)]
[JsonSerializable(typeof(CreateTodo.Request))]
[JsonSerializable(typeof(Todo))]
internal sealed partial class AotJsonContext : JsonSerializerContext { }The default non-AOT path is unchanged for assemblies that do not carry the attribute, preserving full backwards compatibility. See docs/aot.md and the AotSample README.
You do not need WASI or edge hosting to use SliceFx. The default path is still a normal ASP.NET Core app.
Edge usually means running code closer to users on platforms such as Cloudflare Workers or Fermyon Spin instead of only in one central server region. WASI is a standards-based way to package server-side code as a WebAssembly component that those hosts can run. In SliceFx, WASI support proves the portability story: if a feature returns plain request/response records and avoids ASP.NET-only response helpers, the same feature shape can be dispatched outside ASP.NET through a generated route table and SliceFx.Wasi.
That path is intentionally experimental. SliceFx.Wasi depends on preview tooling, has stricter JSON and validation rules, and does not run arbitrary ASP.NET endpoint filters. The practical deployment target today is one wasi:http component with generated in-process route dispatch, not one WASM component per feature. The practical benefit today is visibility: slicefx routes tells you which endpoints are portable, which are partially portable, and which intentionally stay ASP.NET-only.
Slice endpoints work with ASP.NET Core's standard OpenAPI support out of the box. Add Microsoft.AspNetCore.OpenApi, call builder.Services.AddOpenApi(), and map app.MapOpenApi() in the ASP.NET host:
builder.Services.AddSlice();
builder.Services.AddOpenApi();
var app = builder.Build();
app.MapSlices();
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}The Slice route manifest is a separate build-time artifact for portability classification, client generation, and slicefx openapi manifest projections. It complements rather than replaces the ASP.NET Core OpenAPI document. See docs/guides/openapi.md.
| Topic | Details |
|---|---|
| Source generator and route manifest | docs/source-generator.md |
| CLI commands | docs/cli.md |
| Blazor WASM + generated typed client sample | samples/SliceFx.BlazorSample/ |
| OpenAPI integration | docs/guides/openapi.md |
| Native AOT deployment | docs/aot.md |
| Native AOT sample | samples/SliceFx.AotSample/README.md |
| Lambda hosting and function-per-feature Lambda | docs/lambda.md |
| Lambda function-per-feature sample | samples/SliceFx.LambdaFunctionPerFeatureSample/ |
| WASI deploy path | samples/SliceFx.WasiSample/README.md |
| Migration guides | Minimal API, controllers |
| Platform abstraction and DI swap patterns | docs/patterns/platform-abstraction.md |
| ASP.NET features and escape hatches | docs/guides/aspnet-features.md |
| Design decisions FAQ | docs/design-decisions.md |
| Product direction | docs/product-direction.md |
| Production readiness | docs/production-readiness.md |
dotnet build
dotnet run --project samples/SliceFx.SampleThen:
curl http://localhost:5099/health
curl -X POST http://localhost:5099/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
curl -X DELETE http://localhost:5099/users/{id} -H "X-API-Key: secret"MIT. See LICENSE.