Search
K
  1. Native AOT

Native AOT Publishing

A FastEndpoints application can be compiled down to a standalone native binary that can be executed on either Windows, Linux or macOS without the .NET Runtime being present on the machine. Native AOT compiled apps generally tend to be smaller, uses less memory, and starts up faster, at the expense of losing runtime performance optimizations provided by JIT compilation and taking much longer to compile and publish, as well as requiring the developer to jump through a few hoops to ensure compatibility with AOT compilation. FastEndpoints takes away much of the pain with automated source generation, but requires the project to be setup in a certain way.

Install Prerequisites

Visual Studio Users

Ensure Visual Studio 2026 or later is installed along with the Desktop development with C++ workload with all default components.

Rider/VS Code Users

On Windows

  • Download VS Build Tools
  • Download this zip file with two CMD files and extract them to the same folder where vs_BuildTools.exe is located.
  • Execute download.cmd which will first create an offline layout with the required files.
  • Execute install.cmd which will do the actual installation from the created layout.

On Linux

Install the dependencies according to which flavor of Linux you're on:

# Arch
sudo pacman -S --needed base-devel clang zlib krb5 icu

# Debian
sudo apt-get install clang zlib1g-dev

# Alpine
sudo apk add clang build-base zlib-dev

# Fedora/RHEL
sudo dnf install clang zlib-devel zlib-ng-devel zlib-ng-compat-devel

On macOS

Install the Command Line Tools for Xcode.

Project Setup

TIP

If you prefer to scaffold a new project that's already primed for AOT publishing, simply do the following:

terminal
dotnet new install FastEndpoints.TemplatePack
dotnet new feaot -n MyProject

Have a quick read through the following so you know what's involved in developing and publishing a Native AOT application.

If you'd like to start from scratch, create a new Web project and install the necessary nuget packages:

terminal
dotnet new web -n MyWebApp
cd MyWebApp

dotnet add package FastEndpoints
dotnet add package FastEndpoints.Generator
dotnet add package FastEndpoints.Swagger
dotnet add package Scalar.AspNetCore

Enable AOT Publishing

Add the following MSBuild properties to your project file.

MyWebApp.csproj
<PropertyGroup>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization> <!-- optional but recommended -->
</PropertyGroup>

Enable Source Generation

Native AOT publishing requires source generation due to runtime reflection/compilation being unavailable.

JsonSerializerContext Generation

Typically in ASP.NET, when you develop an AOT compiled application, it requires you to manually create and manage STJ JsonSerializerContexts for your endpoints and wire them up yourself in startup code. FastEndpoints.Generator includes a tool that automatically generates the JsonSerializerContexts everytime you build the application. An extension method is also generated which helps to wire it up with the serializer options during startup with a single call.

Enable serializer context generation in the csproj file like so:

MyWebApp.csproj
<PropertyGroup>
    <GenerateSerializerContexts>true</GenerateSerializerContexts>
    <SerializerContextOutputPath>Generated/SerializerCtx</SerializerContextOutputPath> <!-- optional -->
</PropertyGroup>

Serializer context generation is not the same as typical source generation and the generated files must be considered as code you've written yourself and should be checked in to source control. This is due to a limitation in .NET source generation which prevents incremental source generators from being chained. FastEndpoints work around this limitation by using a locally installed development time only CLI tool.

Use Slim Builder and Type Discovery Generator

Use the slim app builder and register the source generator discovered types.

Program.cs
var bld = WebApplication.CreateSlimBuilder(args);
bld.Services.AddFastEndpoints(o => o.SourceGeneratorDiscoveredTypes = DiscoveredTypes.All)

Register Generated SerializerContext & Reflection Data

Hook up the generated serializer contexts and reflection data. The extension method name will have your assembly name at the end. If your endpoints/DTOs are located in separate projects, install the FastEndpoints.Generator library in each one, and call each generated extension method from those projects.

Program.cs
app.UseFastEndpoints(
    c =>
    {
        c.Serializer.Options.AddSerializerContextsFromMyWebApp();
        c.Binding.ReflectionCache.AddFromMyWebApp();
    });

Publish AOT Executable

The above is all that's needed for publishing an AOT compiled executable. Add a couple of endpoints to the app and fire off a "publish" with the following dotnet command. Change the runtime identifier for your target platform as needed. For example: win-x64 for Windows 64-bit.

terminal
dotnet publish MyWebApp.csproj -c Release -r linux-x64 -o dist

Swagger Setup

NOTE

The following information only applies if your app needs to expose swagger documents publicly in the production AOT build. If there's no such need, the configuration can be greatly simplified.

FastEndpoints relies on NSwag for swagger document generation and NSwag is not yet AOT compatible. Until that happens, what's needed is to generate the swagger docs during compile time and serve those pre-built swagger JSON files during runtime. Scalar has to be used for visualizing those generated swagger docs as SwaggerUI cannot be used with AOT compilation.

Define A Swagger Document

Defining swagger documents works as usual, but you must explicitly specify a document name, since the same name is required for exporting docs.

program.cs
bld.Services
   .SwaggerDocument(
       o => o.DocumentSettings =
                d =>
                {
                    d.DocumentName = "v1"; //must match below
                });

Enable Serving Static Files & Swagger Doc Export

app.UseStaticFiles()
app.UseFastEndpoints();
await app.ExportSwaggerDocsAndExitAsync("v1", "v2"); //must match doc names above

The order of these statements are important. Static file middleware > UseFastEndpoints > ExportSwaggerDocsAndExitAsync. The static file middleware is necessary to serve the generated swagger doc JSON files for Scalar to load. The ExportSwaggerDocsAndExitAsync() call must be placed right after UseFastEndpoints().

Swagger doc export works by spinning up an instance of your app in non-AOT mode which allows NSwag swagger generation to work. It immediately exits the program after swagger generation is done, without booting up your app in normal mode. ExportSwaggerDocsAndExitAsync() method only becomes effective during Native AOT publishing and ONLY if you've enabled exporting swagger docs in the csproj file like so:

MyWebApp.csproj
<PropertyGroup>
    <ExportSwaggerDocs>true</ExportSwaggerDocs>
    <SwaggerExportPath>wwwroot/openapi</SwaggerExportPath> <!-- optional -->
</PropertyGroup>

While the app has AOT publishing enabled (in the csproj), it's not possible to get NSwag to generate swagger docs without doing a Native AOT publish (which spins up a non-AOT instance internally as a build step). As a solution to this limitation, you can either force swagger generation with the following command:

terminal
dotnet run --export-swagger-docs true -p:PublishAot=false

Or, by only enabling Native AOT publishing for Release configuration like so:

MyWebApp.csproj
<PropertyGroup Condition="$(Configuration) == 'Release'">
    <PublishAot>true</PublishAot>
</PropertyGroup>

Enabling AOT publishing conditionally like this is the recommended approach, as that allows swagger docs to be generated as usual during runtime for a typical development loop and will automatically publish the app with exported swagger docs when you publish the app in Release mode with AOT compilation.

Enable API Visualization With Scalar

The final piece of the puzzle is to enable Scalar to load the exported swagger JSON files for visualization.

Place the following anywhere after ExportSwaggerDocsAndExitAsync() and before app.Run()

app.UseOpenApi(c => c.Path = "/openapi/{documentName}.json"); // where scalar looks for JSON files
app.MapScalarApiReference(o => o.AddDocument("v1")); // add each swagger document your app has

Simplify Swagger Setup

The above-mentioned hoops only come in to play if the app has to expose swagger documents publicly in the AOT production build. If swagger docs and visualization is only needed during development, the configuration can be simplified like so:

MyWebApp.csproj
<PropertyGroup Condition="$(Configuration) == 'Release'"> <!-- still needed for swagger generation -->
    <PublishAot>true</PublishAot>
</PropertyGroup>

<PropertyGroup>
    <InvariantGlobalization>true</InvariantGlobalization>
    <GenerateSerializerContexts>true</GenerateSerializerContexts>
    <SerializerContextOutputPath>Generated/SerializerCtx</SerializerContextOutputPath>
</PropertyGroup>
program.cs
var bld = WebApplication.CreateSlimBuilder(args);
bld.Services
   .AddFastEndpoints(o => o.SourceGeneratorDiscoveredTypes = DiscoveredTypes.All)
   .SwaggerDocument(
       o => o.DocumentSettings =
                d =>
                {
                    d.DocumentName = "v1";
                });

var app = bld.Build();
app.UseFastEndpoints()
   .UseOpenApi(c => c.Path = "/openapi/{documentName}.json");
app.MapScalarApiReference(o => o.AddDocument("v1"));
app.Run();

© FastEndpoints 2026