Quickstart: Build your first .NET Aspire app

Cloud-native apps often require connections to various services such as databases, storage and caching solutions, messaging providers, or other web services. .NET Aspire is designed to streamline connections and configurations between these types of services. This quickstart shows how to create a .NET Aspire Starter Application template solution.

In this quickstart, you'll explore the following tasks:

  • Create a basic .NET app that is set up to use .NET Aspire.
  • Add and configure a .NET Aspire component to implement caching at project creation time.
  • Create an API and use service discovery to connect to it.
  • Orchestrate communication between a front end UI, a back end API, and a local Redis cache.

Prerequisites

To work with .NET Aspire, you need the following installed locally:

For more information, see .NET Aspire setup and tooling.

Create the .NET Aspire template

To create a new .NET Aspire Starter Application, you can use either Visual Studio or the .NET CLI.

Visual Studio provides .NET Aspire project templates that handle some initial setup configurations for you. Complete the following steps to create a project for this quickstart:

  1. At the top of Visual Studio, navigate to File > New > Project.

  2. In the dialog window, search for Aspire and select .NET Aspire Starter Application. Select Next.

    A screenshot of the .NET Aspire Starter Application template.

  3. On the Configure your new project screen:

    • Enter a Project Name of AspireSample.
    • Leave the rest of the values at their defaults and select Next.
  4. On the Additional information screen:

    • Make sure .NET 8.0 (Long Term Support) is selected.
    • Ensure that Use Redis for caching (requires a supported container runtime) is checked and select Create.
    • Optionally, you can select Create a tests project. For more information, see Testing .NET Aspire apps.

Visual Studio creates a new solution that is structured to use .NET Aspire.

Test the app locally

The sample app is now ready for testing. You want to verify the following:

  • Weather data is retrieved from the API project using service discovery and displayed on the weather page.
  • Subsequent requests are handled via the output caching configured by the .NET Aspire Redis component.

In Visual Studio, set the AspireSample.AppHost project as the startup project by right-clicking on the project in the Solution Explorer and selecting Set as Startup Project. Then, press F5 to run the app.

  1. The app displays the .NET Aspire dashboard in the browser. We'll look at the dashboard in more detail later. For now, find the webfrontend project in the list of resources and select the project's localhost endpoint.

    A screenshot of the .NET Aspire Dashboard, highlighting the webfrontend project's localhost endpoint.

    The home page of the webfrontend app displays "Hello, world!"

  2. Navigate from the home page to the weather page in the using the left side navigation. The weather page displays weather data. Make a mental note of some of the values represented in the forecast table.

  3. Continue occasionally refreshing the page for 10 seconds. Within 10 seconds, the cached data is returned. Eventually, a different set of weather data appears, since the data is randomly generated and the cache is updated.

The Weather page of the webfrontend app showing the weather data retrieved from the API.

🤓 Congratulations! You created and ran your first .NET Aspire application! Now let's investigate the structure and other features of your new .NET Aspire app.

Explore the .NET Aspire dashboard

When you run a .NET Aspire app, a dashboard launches that you use to monitor various parts of your app. The dashboard resembles the following screenshot:

A screenshot of the .NET Aspire Dashboard, depicting the Projects tab.

Visit each link on the left navigation to view different information about the .NET Aspire app:

  • Resources: Lists basic information for all of the individual .NET projects in your .NET Aspire app, such as the app state, endpoint addresses, and the environment variables that were loaded in.

  • Console: Displays the console output from each of the projects in your app.

  • Structured: Displays structured logs in table format. These logs support basic filtering, free-form search, and log level filtering as well. You should see logs from the apiservice and the webfrontend. You can expand the details of each log entry by selecting the View button on the right end of the row.

  • Traces: Displays the traces for your application, which can track request paths through your apps. Locate a request for /weather and select View on the right side of the page. The dashboard should display the request in stages as it travels through the different parts of your app.

    A screenshot showing an .NET Aspire dashboard trace for the webfrontend /weather route.

  • Metrics: Displays various instruments and meters that are exposed and their corresponding dimensions for your app. Metrics conditionally expose filters based on their available dimensions.

    A screenshot showing an Aspire dashboard metrics page for the webfrontend.

For more information, see .NET Aspire dashboard overview.

Understand the .NET Aspire solution structure

The solution consists of the following projects:

  • AspireSample.ApiService: An ASP.NET Core Minimal API project is used to provide data to the front end. This project depends on the shared AspireSample.ServiceDefaults project.
  • AspireSample.AppHost: An orchestrator project designed to connect and configure the different projects and services of your app. The orchestrator should be set as the Startup project, and it depends on the AspireSample.ApiService and AspireSample.Web projects.
  • AspireSample.ServiceDefaults: A .NET Aspire shared project to manage configurations that are reused across the projects in your solution related to resilience, service discovery, and telemetry.
  • AspireSample.Web: An ASP.NET Core Blazor App project with default .NET Aspire service configurations, this project depends on the AspireSample.ServiceDefaults project. For more information, see .NET Aspire service defaults.

Your AspireSample directory should resemble the following:

└───📂 AspireSample
     ├───📂 AspireSample.ApiService
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.ApiService.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.AppHost
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.AppHost.csproj
     │    └─── Program.cs
     ├───📂 AspireSample.ServiceDefaults
     │    ├─── AspireSample.ServiceDefaults.csproj
     │    └─── Extensions.cs
     ├───📂 AspireSample.Web
     │    ├───📂 Components
     │    │    ├───📂 Layout
     │    │    │    ├─── MainLayout.razor
     │    │    │    ├─── MainLayout.razor.css
     │    │    │    ├─── NavMenu.razor
     │    │    │    └─── NavMenu.razor.css
     │    │    ├───📂 Pages
     │    │    │    ├─── Counter.razor
     │    │    │    ├─── Error.razor
     │    │    │    ├─── Home.razor
     │    │    │    └─── Weather.razor
     │    │    ├─── _Imports.razor
     │    │    ├─── App.razor
     │    │    └─── Routes.razor
     │    ├───📂 Properties
     │    │    └─── launchSettings.json
     │    ├───📂 wwwroot
     │    │    ├───📂 bootstrap
     │    │    │    ├─── bootstrap.min.css
     │    │    │    └─── bootstrap.min.css.map
     │    │    ├─── app.css
     │    │    └─── favicon.png
     │    ├─── appsettings.Development.json
     │    ├─── appsettings.json
     │    ├─── AspireSample.Web.csproj
     │    ├─── Program.cs
     │    └─── WeatherApiClient.cs
     └─── AspireSample.sln

Explore the starter projects

Each project in an .NET Aspire solution plays a role in the composition of your app. The *.Web project is a standard ASP.NET Core Blazor App that provides a front end UI. For more information, see New Blazor Web App template. The *.ApiService project is a standard ASP.NET Core Minimal API template project. Both of these projects depend on the *.ServiceDefaults project, which is a shared project that's used to manage configurations that are reused across projects in your solution.

The two projects of interest in this quickstart are the *.AppHost and *.ServiceDefaults projects detailed in the following sections.

.NET Aspire app host project

The *.AppHost project is responsible for acting as the orchestrator, and sets the IsAspireHost property of the project file to true:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsAspireHost>true</IsAspireHost>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\AspireSample.ApiService\AspireSample.ApiService.csproj" />
    <ProjectReference Include="..\AspireSample.Web\AspireSample.Web.csproj" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0-preview.6.24214.1" />
    <PackageReference Include="Aspire.Hosting.Redis" Version="8.0.0-preview.6.24214.1" />
  </ItemGroup>

</Project>

Consider the Program.cs file of the AspireSample.AppHost project:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

var apiservice = builder.AddProject<Projects.AspireSample_ApiService>("apiservice");

builder.AddProject<Projects.AspireSample_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.Build().Run();

If you've used either the .NET Generic Host or the ASP.NET Core Web Host before, the app host programming model and builder pattern should be familiar to you. The preceding code:

  • Creates an IDistributedApplicationBuilder instance from calling DistributedApplication.CreateBuilder().
  • Calls AddRedis with the name "cache" to add a Redis server to the app, assigning the returned value to a variable named cache, which is of type IResourceBuilder<RedisResource>.
  • Calls AddProject given the generic-type parameter with the project's details, adding the AspireSample.ApiService project to the application model. This is one of the fundamental building blocks of .NET Aspire, and it's used to configure service discovery and communication between the projects in your app. The name argument "apiservice" is used to identify the project in the application model, and used later by projects that want to communicate with it.
  • Calls AddProject again, this time adding the AspireSample.Web project to the application model. It also chains multiple calls to WithReference passing the cache and apiservice variables. The WithReference API is another fundamental API of .NET Aspire, which injects either service discovery information or connection string configuration into the project being added to the application model.

Finally, the app is built and run. The DistributedApplication.Run() method is provided by the .NET Aspire SDK, and is responsible for starting the app and all of its dependencies. For more information, see .NET Aspire orchestration overview.

.NET Aspire service defaults project

The *.ServiceDefaults project is a shared project that's used to manage configurations that are reused across the projects in your solution. This project ensures that all dependent services share the same resilience, service discovery, and OpenTelemetry configuration. A shared .NET Aspire project file contains the IsAspireSharedProject property set as true:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsAspireSharedProject>true</IsAspireSharedProject>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />

    <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.4.0" />
    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0-preview.6.24214.1" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
  </ItemGroup>

</Project>

The service defaults project exposes an extension method on the IHostApplicationBuilder type, named AddServiceDefaults. The service defaults project from the template is a starting point, and you can customize it to meet your needs. For more information, see .NET Aspire service defaults.

Orchestrate service communication

.NET Aspire provides orchestration features to assist with configuring connections and communication between the different parts of your app. The AspireSample.AppHost project added the AspireSample.ApiService and AspireSample.Web projects to the application model. It also declared their names as "webfrontend" for Blazor front end, "apiservice" for the API project reference. Additionally, a Redis server resource labelled "cache" was added. These names are used to configure service discovery and communication between the projects in your app.

The front end app defines a typed HttpClient that's used to communicate with the API project.

namespace AspireSample.Web;

public class WeatherApiClient(HttpClient httpClient)
{
    public async Task<WeatherForecast[]> GetWeatherAsync()
    {
        return await httpClient.GetFromJsonAsync<WeatherForecast[]>("/weatherforecast") ?? [];
    }
}

public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

The HttpClient is configured to use service discovery, consider the following code from the Program.cs file of the AspireSample.Web project:

using AspireSample.Web;
using AspireSample.Web.Components;

var builder = WebApplication.CreateBuilder(args);

// Add service defaults & Aspire components.
builder.AddServiceDefaults();
builder.AddRedisOutputCache("cache");

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddHttpClient<WeatherApiClient>(
    client => client.BaseAddress = new("http://apiservice"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
}

app.UseStaticFiles();

app.UseAntiforgery();

app.UseOutputCache();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();
    
app.MapDefaultEndpoints();

app.Run();

The preceding code:

  • Calls AddServiceDefaults, configuring the shared defaults for the app.
  • Calls AddRedisOutputCache with the same connectionName that was used when adding the Redis container "cache" to the application model. This configures the app to use Redis for output caching.
  • Calls AddHttpClient and configures the HttpClient.BaseAddress to be "http://apiservice". This is the name that was used when adding the API project to the application model, and with service discovery configured, it will automatically resolve to the correct address to the API project.

For more information, see Make HTTP requests with the HttpClient class.

Next steps