<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-AU" xmlns:media="http://search.yahoo.com/mrss/">
  <id>https://david.gardiner.net.au/tags/Aspire.xml</id>
  <title type="html">David Gardiner - Aspire</title>
  <updated>2026-05-19T00:35:58.096Z</updated>
  <subtitle>Blog posts tagged with &apos;Aspire&apos; - A blog of software development, .NET and other interesting things</subtitle>
  <rights>Copyright 2026 David Gardiner</rights>
  <icon>https://www.gravatar.com/avatar/37edf2567185071646d62ba28b868fab?s=64</icon>
  <logo>https://www.gravatar.com/avatar/37edf2567185071646d62ba28b868fab?s=256</logo>
  <generator uri="https://github.com/flcdrg/astrojs-atom" version="3">astrojs-atom</generator>
  <author>
    <name>David Gardiner</name>
  </author>
  <link href="https://david.gardiner.net.au/tags/Aspire.xml" rel="self" type="application/atom+xml"/>
  <link href="https://david.gardiner.net.au/tags/Aspire" rel="alternate" type="text/html" hreflang="en-AU"/>
  <category term="Aspire"/>
  <category term="Software Development"/>
  <entry>
    <id>https://david.gardiner.net.au/2026/04/vs-debugging-fatal-error</id>
    <updated>2026-04-10T12:00:00.000+09:30</updated>
    <title>Fatal error 0x8013132d debugging .NET 10 applications in Visual Studio</title>
    <link href="https://david.gardiner.net.au/2026/04/vs-debugging-fatal-error" rel="alternate" type="text/html" title="Fatal error 0x8013132d debugging .NET 10 applications in Visual Studio"/>
    <category term=".NET"/>
    <category term="Aspire"/>
    <category term="Visual Studio"/>
    <published>2026-04-10T12:00:00.000+09:30</published>
    <summary type="html">A workaround for a fatal error while trying to debug a .NET 10 application in Visual Studio</summary>
    <content type="html">&lt;p&gt;Trying to debug an &lt;a href=&quot;https://aspire.dev&quot;&gt;Aspire&lt;/a&gt; .NET 10 application in Visual Studio today and hitting this error:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/visual-studio-fatal-error.B1dWqQi6_Zzh0OH.webp&quot; alt=&quot;Visual Studio error dialog&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A fatal error has occurred and debugging needs to be terminated. For more details, please see the Microsoft Help and Support web site. HRESULT=0x8013132d. ErrorCode=0x0.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A bit of searching online turned up &lt;a href=&quot;https://developercommunity.microsoft.com/t/11022347&quot;&gt;this bug report&lt;/a&gt; on the Microsoft Developer Community site.&lt;/p&gt;
&lt;p&gt;That was eventually forwarded to the &lt;a href=&quot;https://github.com/dotnet/runtime/issues/124913&quot;&gt;.NET runtime&lt;/a&gt; repo on GitHub where it was identified as relating to a &lt;a href=&quot;https://learn.microsoft.com/dotnet/core/compatibility/interop/9.0/cet-support?WT.mc_id=DOP-MVP-5001655&quot;&gt;breaking change introduced in .NET 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This change improves the security of .NET applications, but in this case is stopping me from being able to debug.&lt;/p&gt;
&lt;p&gt;You can opt out of this new behaviour by adding the following property to your csproj:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;CETCompat&amp;gt;false&amp;lt;/CETCompat&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given in my case the problem only happens when I&apos;m debugging, then it would be preferable to leave this new feature on by default, so I&apos;ve added a condition to the property like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;CETCompat Condition=&quot;&apos;$(Configuration)&apos; == &apos;Debug&apos;&quot;&amp;gt;false&amp;lt;/CETCompat&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my Aspire AppHost csproj file, the &lt;code&gt;PropertyGroup&lt;/code&gt; element looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net10.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;UserSecretsId&amp;gt;00000000-0000-0000-0000-000000000000&amp;lt;/UserSecretsId&amp;gt;
    &amp;lt;CETCompat Condition=&quot;&apos;$(Configuration)&apos; == &apos;Debug&apos;&quot;&amp;gt;false&amp;lt;/CETCompat&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/visual-studio-2026.VSY9MLYp.png" width="256" height="256"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/visual-studio-2026.VSY9MLYp.png" width="256" height="256"/>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2025/11/aspire-without-dotnet</id>
    <updated>2025-11-17T08:00:00.000+10:30</updated>
    <title>Aspire with Python, React, Rust and Node apps</title>
    <link href="https://david.gardiner.net.au/2025/11/aspire-without-dotnet" rel="alternate" type="text/html" title="Aspire with Python, React, Rust and Node apps"/>
    <category term=".NET"/>
    <category term="Aspire"/>
    <category term="Talks"/>
    <published>2025-11-17T08:00:00.000+10:30</published>
    <summary type="html">Using Aspire to build a distributed application with Python, React, Rust and Node.js components</summary>
    <content type="html">&lt;p&gt;Aspire (formerly .NET Aspire) is a great way to create observable, production-ready distributed apps by defining a code-based model of services, resources, and connections. It simplifies local development and debugging, as well as deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl_2gR0dV.webp&quot; alt=&quot;Aspire logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;By their very nature, distributed applications will have at least a few (if not a lot) of components. This presents a challenge both for the local developer experience and for deployment. Aspire seeks to simplify this by allowing you to model the services, resources, and connections in code. With one command you can then not only launch everything locally, but ensure that each service knows how to connect to the other services it needs to function.&lt;/p&gt;
&lt;p&gt;You can use it for both development and deployment, or if you already have an existing deployment process you&apos;re happy with (eg. Infrastructure as Code/deployment pipelines) you can just use Aspire to simply your local development experience.&lt;/p&gt;
&lt;p&gt;Check out my previous post &lt;a href=&quot;/2025/11/aspire&quot;&gt;Introducing Aspire&lt;/a&gt; for an overview of what Aspire is and how it works.&lt;/p&gt;
&lt;p&gt;As part of this year&apos;s .NET Conf virtual conference, I presented a talk &quot;Taking .NET out of .NET Aspire - working with non-.NET applications&quot;, in which I show how despite Aspire being written in .NET, it can integrate with a whole range of other software languages ecosystems. Here&apos;s the recording of that presentation:&lt;/p&gt;

&lt;p&gt;One issue with the talk was that it had to be pre-recorded a couple of weeks beforehand, so it was done using the Aspire 9.5 bits. Now that Aspire 13.0 is out, some of the packages that I used from the Community Toolkit in the demo are no longer necessary as Aspire has improved the integration with Python and Node.js applications.&lt;/p&gt;
&lt;p&gt;The source code for the demo can be found at &lt;a href=&quot;https://github.com/flcdrg/aspire-non-dotnet&quot;&gt;https://github.com/flcdrg/aspire-non-dotnet&lt;/a&gt;. A quick glance and you might think &quot;there&apos;s no .NET or Aspire in this repo&quot; and if you just look at the &lt;code&gt;main&lt;/code&gt; branch then you&apos;d be right. There are separate branches where I incrementally integrate each component into Aspire.&lt;/p&gt;
&lt;p&gt;The scenario I demo is a &apos;Pet supplies&apos; e-commerce website, with a React front-end, Python backend API running with MongoDB. The Python backend also talks to a Rust-based payment gateway service, and as a bonus step right at the end I add a Node.js server app to provide random pet jokes!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-petstore-web.NuLNUsqZ_Z2ud4JN.webp&quot; alt=&quot;Screenshot of Pet supplies web page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The technology mix is not unheard of, particularly at some larger organisations where you may have different teams working on different features or services and sometimes they are allowed enough autonomy to choose their own technology stack. Whether this is a case of &quot;using the right tool for the job&quot; or &quot;must use the current shiny new thing&quot; isn&apos;t so important. The reality is that for good or other reasons you often find yourself in this situation.&lt;/p&gt;
&lt;h2&gt;MongoDB&lt;/h2&gt;
&lt;p&gt;MongoDB is supported out of the box in Aspire. The original process was to launch this via &lt;code&gt;docker compose&lt;/code&gt;. Aspire actually &lt;a href=&quot;https://aspire.dev/integrations/compute/docker/&quot;&gt;supports compose files too (via the Aspire.Hosting.Docker package)&lt;/a&gt;, but in this case I&apos;m taking advantage of the &lt;a href=&quot;https://aspire.dev/integrations/databases/mongodb/&quot;&gt;&lt;code&gt;Aspire.Hosting.MongoDB&lt;/code&gt;&lt;/a&gt; to configure the MongoDB service, database, and for bonus points, include the Mongo Express management UI. This will still run in a Docker container, but Aspire handles all the configuration for me.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var mongo = builder.AddMongoDB(&quot;mongo&quot;)
    .WithDataVolume()
    .WithMongoExpress();

var mongodb = mongo.AddDatabase(&quot;petstore&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WithDataVolume()&lt;/code&gt; means that a Docker volume is created to persist the database data between runs.&lt;/p&gt;
&lt;p&gt;The original implementation also has a PowerShell script to populate the database with sample data. I wired up that script so that it gets run automatically when the MongoDB service starts up in Aspire.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var loadData = builder.AddExecutable(&quot;load-data&quot;, &quot;pwsh&quot;, &quot;../mongodb&quot;, &quot;-noprofile&quot;, &quot;./populate.ps1&quot;)
    .WaitFor(mongo)
    .WithArgs(&quot;-connectionString&quot;)
    .WithArgs(new ConnectionStringReference(mongo.Resource, false));
//.WithExplicitStart();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script conveniently already had a parameter defined to pass in a connection string, so I take advantage of that.&lt;/p&gt;
&lt;p&gt;If you&apos;d prefer to just run the script manually (rather than every time you start Aspire) you could uncomment the &lt;code&gt;.WithExplicitStart()&lt;/code&gt; method.&lt;/p&gt;
&lt;h2&gt;Rust&lt;/h2&gt;
&lt;p&gt;I&apos;ve never used the Rust programming language before, but it is becoming increasingly popular, especially where you might previously have used C or C++. Rust uses &lt;a href=&quot;https://doc.rust-lang.org/cargo/&quot;&gt;Cargo&lt;/a&gt; as its package manager and build tool. Support for building and running Rust applications is provided by the &lt;a href=&quot;https://aspire.dev/integrations/frameworks/rust/&quot;&gt;Community Toolkit&apos;s CommunityToolkit.Aspire.Hosting.Rust package&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Configuring the Rust application is quite straightforward with the &lt;code&gt;AddRustApp&lt;/code&gt; method. The application has a default port it listens on but allows that to be overridden via the &lt;code&gt;PAYMENT_API_PORT&lt;/code&gt; environment variable. Aspire will set that to the appropriate port number by the call to &lt;code&gt;WithHttpEndpoint&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var rust = builder.AddRustApp(&quot;rustpaymentapi&quot;, &quot;../RustPaymentApi&quot;, [])
    .WithHttpEndpoint(env: &quot;PAYMENT_API_PORT&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Node.js&lt;/h2&gt;
&lt;p&gt;JavaScript is well known as a front end language, but platforms like Node.js allow you to write server-side applications in JavaScript (and TypeScript) too. Aspire 13.0 introduces improved support for JavaScript apps via a new &lt;a href=&quot;https://www.nuget.org/packages/Aspire.Hosting.JavaScript&quot;&gt;Aspire.Hosting.JavaScript package&lt;/a&gt;. This includes the ability to configure using &lt;code&gt;pnpm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; package managers (the default being &lt;code&gt;npm&lt;/code&gt;).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var nodeApp = builder.AddJavaScriptApp(&quot;node-joke-api&quot;, &quot;../NodeApp&quot;, &quot;start&quot;)
    .WithPnpm()
    // If you are using fnm for Node.js version management, you might need to adjust the PATH
    .WithEnvironment(&quot;PATH&quot;, Environment.GetEnvironmentVariable(&quot;PATH&quot;) + &quot;;&quot; + Environment.ExpandEnvironmentVariables(@&quot;%USERPROFILE%\AppData\Roaming\fnm\aliases\default&quot;))
    .WithHttpEndpoint(env: &quot;PORT&quot;)
    .WithOtlpExporter();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use &lt;a href=&quot;https://github.com/Schniz/fnm&quot;&gt;&lt;code&gt;fnm&lt;/code&gt;&lt;/a&gt; (Fast Node Manager) to manage my Node.js versions. This means that the actual &lt;code&gt;node&lt;/code&gt; executable is not in the PATH by default, but rather is added (or updated) dynamically as I change directory via my PowerShell profile script. Because that script isn&apos;t run by Aspire, I explicity append the &lt;code&gt;fnm&lt;/code&gt; default alias directory to the PATH environment variable so that &lt;code&gt;node&lt;/code&gt; can be found.&lt;/p&gt;
&lt;h2&gt;Python&lt;/h2&gt;
&lt;p&gt;Python is an interesting ecosystem. There are a number of different package managers that will influence how you work with it. Aspire has first-class support for Python applications that use &lt;code&gt;pip&lt;/code&gt; via the &lt;code&gt;Aspire.Hosting.Python&lt;/code&gt; NuGet package. (See &lt;a href=&quot;https://aspire.dev/get-started/first-app/?lang=python&quot;&gt;Python apps in Aspire&lt;/a&gt; for more details).&lt;/p&gt;
&lt;p&gt;I recently worked on a client engagement where they were using &lt;a href=&quot;https://docs.astral.sh/uv/&quot;&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt; with their Python applications. Aspire 13.0 now includes direct support for &lt;code&gt;uv&lt;/code&gt; (via &lt;code&gt;WithUv()&lt;/code&gt;) and &lt;code&gt;uvicorn&lt;/code&gt; (with &lt;code&gt;AddUvicornApp()&lt;/code&gt;):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var pythonApp = builder.AddUvicornApp(&quot;python-api&quot;, &quot;../PythonUv&quot;, &quot;src.api:app&quot;)
    .WithUv()
    .WaitFor(mongo)
    .WaitFor(rust)
    .WaitFor(nodeApp)
    .WithEnvironment(&quot;PYTHONIOENCODING&quot;, &quot;utf-8&quot;)
    .WithEnvironment(&quot;MONGO_CONNECTION_STRING&quot;, new ConnectionStringReference(mongo.Resource, false))
    .WithEnvironment(&quot;PAYMENT_API_BASE_URL&quot;, new EndpointReference(rust.Resource, &quot;http&quot;))
    .WithEnvironment(&quot;NODE_APP_BASE_URL&quot;, ReferenceExpression.Create($&quot;{nodeApp.Resource.GetEndpoint(&quot;http&quot;)}&quot;))
    .WithHttpHealthCheck(&quot;/&quot;)
    .WithExternalHttpEndpoints();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We wait for the MongoDB service, the Rust payment service, and the Node.js joke service, so that they have all started before the Python app. We also set up environment variables to pass in the connection strings and endpoints that the Python app needs to connect to those services.&lt;/p&gt;
&lt;h2&gt;A Vite React Frontend web app&lt;/h2&gt;
&lt;p&gt;The same &lt;code&gt;Aspire.Hosting.JavaScript&lt;/code&gt; package that we used to wire up the Node.js application can also be used for the frontend web app. Helpfully, it specifically includes support for Vite apps&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var web = builder.AddViteApp(&quot;web&quot;, &quot;../web-vite-react&quot;)
    .WithPnpm()
    // If you are using fnm for Node.js version management, you might need to adjust the PATH
    .WithEnvironment(&quot;PATH&quot;, Environment.GetEnvironmentVariable(&quot;PATH&quot;) + &quot;;&quot; + Environment.ExpandEnvironmentVariables(@&quot;%USERPROFILE%\AppData\Roaming\fnm\aliases\default&quot;))
    .WaitFor(pythonApp)
    .WithEnvironment(&quot;VITE_API_BASE_URL&quot;, new EndpointReference(pythonApp.Resource, &quot;http&quot;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, because I use &lt;code&gt;fnm&lt;/code&gt; to manage my Node.js versions, I need to append the &lt;code&gt;fnm&lt;/code&gt; default alias directory to the PATH environment variable so that &lt;code&gt;node&lt;/code&gt; can be found.&lt;/p&gt;
&lt;p&gt;The frontend application only talks to the Python backend API, so we enusre that service has started first, and set up the &lt;code&gt;VITE_API_BASE_URL&lt;/code&gt; environment variable so that the Vite app knows where to find the API.&lt;/p&gt;
&lt;p&gt;And with that in place, we can run the entire distributed application locally with a single command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet run --project ./AspireAppHost/AspireAppHost.csproj --launch-profile http
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not all of the applications I&apos;m using here support HTTPS self-signed development certificates, so I stick with running everything over HTTP for now. This is something that Aspire has improved on in 13.0. Obviously in a production deployment you&apos;d want to use HTTPS everywhere. If I didn&apos;t need to set the launch profile, then I could use the Aspire CLI instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aspire run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The nice thing about all this is I didn&apos;t need to make any changes any of the front end or back end applications to get them to work with Aspire. All the configuration was done in the Aspire host application. This assumes that your applications have provided a &apos;seam&apos; (eg. environment variables or command line arguments) to allow you to configure things like connection strings, ports, and endpoints.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-petstore-resources.CkupUbS-_Z1nuOUe.webp&quot; alt=&quot;Screenshot of Aspire dashboard showing resources page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the final version of AppHost.cs:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var builder = DistributedApplication.CreateBuilder(args);

// MongoDB
var mongo = builder.AddMongoDB(&quot;mongo&quot;)
    .WithDataVolume()
    .WithMongoExpress();

var mongodb = mongo.AddDatabase(&quot;petstore&quot;);

var loadData = builder.AddExecutable(&quot;load-data&quot;, &quot;pwsh&quot;, &quot;../mongodb&quot;, &quot;-noprofile&quot;, &quot;./populate.ps1&quot;)
    .WaitFor(mongo)
    .WithArgs(&quot;-connectionString&quot;)
    .WithArgs(new ConnectionStringReference(mongo.Resource, false));
//.WithExplicitStart();

// Rust service
var rust = builder.AddRustApp(&quot;rustpaymentapi&quot;, &quot;../RustPaymentApi&quot;, [])
    .WithHttpEndpoint(env: &quot;PAYMENT_API_PORT&quot;);

// Node.js App
var nodeApp = builder.AddJavaScriptApp(&quot;node-joke-api&quot;, &quot;../NodeApp&quot;, &quot;start&quot;)
    .WithPnpm()
    // If you are using fnm for Node.js version management, you might need to adjust the PATH
    .WithEnvironment(&quot;PATH&quot;, Environment.GetEnvironmentVariable(&quot;PATH&quot;) + &quot;;&quot; + Environment.ExpandEnvironmentVariables(@&quot;%USERPROFILE%\AppData\Roaming\fnm\aliases\default&quot;))
    .WithHttpEndpoint(env: &quot;PORT&quot;)
    .WithOtlpExporter();

// Python API
var pythonApp = builder.AddUvicornApp(&quot;python-api&quot;, &quot;../PythonUv&quot;, &quot;src.api:app&quot;)
    .WithUv()
    .WaitFor(mongo)
    .WaitFor(rust)
    .WaitFor(nodeApp)
    .WithEnvironment(&quot;PYTHONIOENCODING&quot;, &quot;utf-8&quot;)
    .WithEnvironment(&quot;MONGO_CONNECTION_STRING&quot;, new ConnectionStringReference(mongo.Resource, false))
    .WithEnvironment(&quot;PAYMENT_API_BASE_URL&quot;, new EndpointReference(rust.Resource, &quot;http&quot;))
    .WithEnvironment(&quot;NODE_APP_BASE_URL&quot;, ReferenceExpression.Create($&quot;{nodeApp.Resource.GetEndpoint(&quot;http&quot;)}&quot;))
    .WithHttpHealthCheck(&quot;/&quot;)
    .WithExternalHttpEndpoints();

// Frontend
var web = builder.AddViteApp(&quot;web&quot;, &quot;../web-vite-react&quot;)
    .WithPnpm()
    // If you are using fnm for Node.js version management, you might need to adjust the PATH
    .WithEnvironment(&quot;PATH&quot;, Environment.GetEnvironmentVariable(&quot;PATH&quot;) + &quot;;&quot; + Environment.ExpandEnvironmentVariables(@&quot;%USERPROFILE%\AppData\Roaming\fnm\aliases\default&quot;))
    .WaitFor(pythonApp)
    .WithEnvironment(&quot;VITE_API_BASE_URL&quot;, new EndpointReference(pythonApp.Resource, &quot;http&quot;));

builder.Build().Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get the full benefit of Aspire you will want to take a look at adding OpenTelemetry instrumentation. If you already have that in place then there&apos;s probably nothing to change. Aspire will set a bunch of OpenTelemetry-related environment variables for each application. If that&apos;a new thing then you&apos;ll get the double benefit of then being able to take advantage of that telemetry when your applications are deployed to production too!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-petstore-traces.ZXy-RHuR_Z1wOzaQ.webp&quot; alt=&quot;Screenshot of Aspire dashboard showing traces page&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-petstore-metrics.B07KqI6V_Z1h2lSh.webp&quot; alt=&quot;Screenshot of Aspire dashboard showing metrics page&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Aspire has the potential to greatly simplify and improve the local development experience for distributed applications - potentially removing the need for numerous scripts and manual steps for a developer to get up and running much more quickly. It&apos;s also a tool that can benefit almost every development team, regardless of the technology stack they are using.&lt;/p&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl.png" width="256" height="256"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl.png" width="256" height="256"/>
  </entry>
  <entry>
    <id>https://david.gardiner.net.au/2025/11/aspire</id>
    <updated>2025-11-12T09:00:00.000+10:30</updated>
    <title>Introducing Aspire</title>
    <link href="https://david.gardiner.net.au/2025/11/aspire" rel="alternate" type="text/html" title="Introducing Aspire"/>
    <category term=".NET"/>
    <category term="Aspire"/>
    <published>2025-11-12T09:00:00.000+10:30</published>
    <summary type="html">What is Aspire, and how can it help you orchestrate your local development experience
by modelling your application architecture, and providing a really cool dashboard (among other things)?</summary>
    <content type="html">&lt;p&gt;&lt;a href=&quot;https://aspire.dev/&quot;&gt;Aspire&lt;/a&gt; is a really interesting tool for orchestrating your local development experience, integrating with OpenTelemetry, and optionally providing a way to automate deployments. It has the potential to replace manual steps and custom startup scripts with a more declarative and simpler approach.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl_2gR0dV.webp&quot; alt=&quot;Aspire logo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Originally known as &apos;.NET Aspire&apos;, the name has recently been tweaked to just &apos;Aspire&apos; to help promote it&apos;s applicability across all kinds of development environments, not just .NET.&lt;/p&gt;
&lt;p&gt;Today the latest version of Aspire was demonstrated at &lt;a href=&quot;https://www.dotnetconf.net/agenda&quot;&gt;.NET Conf 2025&lt;/a&gt;. Let&apos;s take a look at what Aspire is, and how it can help you.&lt;/p&gt;
&lt;h2&gt;The AppHost&lt;/h2&gt;
&lt;p&gt;At the heart of Aspire is the concept of an &apos;AppHost&apos;. This is a .NET project in which you define the services (which could be local applications, containers and even remote services). You also define any dependencies between those services, and can use that to then provide configuration information (connection strings, endpoint URLs etc) so that they can communicate with each other. You&apos;re essentially creating a model of your application architecture.&lt;/p&gt;
&lt;p&gt;Importantly, while the AppHost is a .NET project (possibly even a .NET 10 file-based application to make it really simple), this is the only thing that needs .NET. All the other services can be written in pretty much any language or framework you like. They could be Node.js applications, Python, Java, or anything else (including .NET of course!).&lt;/p&gt;
&lt;p&gt;Here&apos;s an example AppHost file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var builder = DistributedApplication.CreateBuilder(args);

var mongo = builder.AddMongoDB(&quot;mongo&quot;)
    .WithDataVolume();

var api = builder.AddProject&amp;lt;Projects.aspire_starter_ApiService&amp;gt;(&quot;apiservice&quot;)
    .WithReference(mongo)
    .WaitFor(mongo);

var frontend = builder.AddViteApp(&quot;frontend&quot;, &quot;../frontend&quot;)
    .WithPnpm()
    .WithReference(api)
    .WaitFor(api);

builder.Build().Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In just those ~15 lines of code, we&apos;re describing an application which has a MongoDB database, a .NET API service, and a Vite frontend web app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We create a &lt;code&gt;builder&lt;/code&gt; variable (an instance of &lt;code&gt;IDistributedApplicationBuilder&lt;/code&gt;) that will then use to define and wire up the services.&lt;/li&gt;
&lt;li&gt;The MongoDB database is hosted as a Docker container, with a data volume to persist data between runs.&lt;/li&gt;
&lt;li&gt;The API service is a .NET project that references the MongoDB service and waits for it to be ready before starting.&lt;/li&gt;
&lt;li&gt;The frontend is a Vite application that references the API service and exposes an HTTP endpoint.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that in place, all you need to do is run this AppHost project (either with &lt;code&gt;dotnet run&lt;/code&gt; or via the Aspire CLI with &lt;code&gt;aspire run&lt;/code&gt;), and Aspire will figure out the order to start the services, then start them up while providing the requested configuration information.&lt;/p&gt;
&lt;h3&gt;WaitFor&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;WaitFor&lt;/code&gt; method tells Aspire that this service should wait until the referenced service is in a &apos;running&apos; state. Services may define health checks that Aspire will use to determine when a service is truly ready. SQL Server is a good example - where even though the main SQL Server process may have started, the database engine won&apos;t be ready immediately as there are a number of housekeeping tasks it needs to complete before all the databases become available for use.&lt;/p&gt;
&lt;h3&gt;WithReference&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;WithReference&lt;/code&gt; method causes Aspire to inject environment variables into the service which provide a way for it to connect to the referenced service.&lt;/p&gt;
&lt;p&gt;For the above code, the references to Mongo generate connection string environment variable of the form &lt;code&gt;ConnectionStrings__mongo&lt;/code&gt;. The API service can use that environment variable to connect to the MongoDB database.&lt;/p&gt;
&lt;p&gt;The reference to the API service generate environment variables &lt;code&gt;APISERVICE_HTTP&lt;/code&gt; and &lt;code&gt;APISERVICE_HTTPS&lt;/code&gt; which provide the URLs that the frontend can use to connect to the API.&lt;/p&gt;
&lt;p&gt;There are overloads of &lt;code&gt;WithReference&lt;/code&gt; that allow you to customise naming scheme of the environment variables, and if that isn&apos;t enough you can always add a custom environment variable with &lt;code&gt;WithEnvironment&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The dashboard&lt;/h2&gt;
&lt;p&gt;One of the key features of Aspire is that you&apos;re not just left to wonder how things are going. A really cool dashboard is provided that gives you both the high-level overview of all your services, as well as being able to drill into specific service configuration and logs, and even see OpenTelemetry traces and metrics (if you have OpenTelemetry added to your services).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-dashboard.YGmYruy3_Zxr80d.webp&quot; alt=&quot;Aspire dashboard showing running resources&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the &apos;frontend&apos; service shows the configuration that Aspire has provided to that service, including the environment variables that were injected:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-service-details.LwT7lW-j_Z1N3e7e.webp&quot; alt=&quot;Viewing a service&apos;s configuration&quot; /&gt;&lt;/p&gt;
&lt;p&gt;View all the console output from services (or filter to just a specific service):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-console.Ba6dWfxd_Z7b1R3.webp&quot; alt=&quot;Aspire dashboard showing aggregated console output&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If your services are instrumented with OpenTelemetry, you can see structured logs, traces and metrics:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-structured-logging.D8djTE9C_15411K.webp&quot; alt=&quot;Aspire dashboard showing structured logging&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-traces.CyGBFzle_ZdYPRv.webp&quot; alt=&quot;Aspire dashboard showing traces&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-starter-metrics.DuU2P7n4_mqBU7.webp&quot; alt=&quot;Aspire dashboard showing metrics&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Aspire is resilient enough that for example if you forget to start Docker, it will let you know that services depending on that can&apos;t start.
&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-dashboard-unhealthy.u_OIjb65_Z29pbV8.webp&quot; alt=&quot;Aspire dashboard showing unhealthy state&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The cool thing though is you don&apos;t need to restart Aspire. If you start Docker, Aspire will notice that it is now available, and will start the services that were waiting on it. You can see the status change in real-time in the dashboard as services become healthy. Note that the other services (which need to wait for Mongo) are not even started yet.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://david.gardiner.net.au/_astro/aspire-dashboard-becoming-health.D_vLN0tV_Z51bD1.webp&quot; alt=&quot;Aspire dashboard showing becoming healthy state&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Publishing and deploying&lt;/h2&gt;
&lt;p&gt;Aspire can optionally prepare the application for deployment by &apos;publishing&apos; parameterised assets. You can then &apos;deploy&apos; those assets to a specific target environment.&lt;/p&gt;
&lt;p&gt;Currently, the following deployment targets are supported:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker / Docker Compose&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;Azure Container Apps&lt;/li&gt;
&lt;li&gt;Azure App Services&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some service types may support these targets natively, while others may need additional configuration. There are also non-Azure targets available too. For example there&apos;s support for AWS with the &lt;a href=&quot;https://www.nuget.org/packages/Aspire.Hosting.AWS/&quot;&gt;Aspire.Hosting.AWS&lt;/a&gt; package. See &lt;a href=&quot;https://github.com/aws/integrations-on-dotnet-aspire-for-aws&quot;&gt;https://github.com/aws/integrations-on-dotnet-aspire-for-aws&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;For example, adding this line to the AppHost above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;builder.AddDockerComposeEnvironment(&quot;aspire-starter&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then running the publish command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;aspire publish
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;causes a &lt;code&gt;docker-compose.yaml&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; files to be created:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  aspire-starter-dashboard:
    image: &quot;mcr.microsoft.com/dotnet/nightly/aspire-dashboard:latest&quot;
    expose:
      - &quot;18888&quot;
      - &quot;18889&quot;
    networks:
      - &quot;aspire&quot;
    restart: &quot;always&quot;
  mongo:
    image: &quot;docker.io/library/mongo:8.0&quot;
    environment:
      MONGO_INITDB_ROOT_USERNAME: &quot;admin&quot;
      MONGO_INITDB_ROOT_PASSWORD: &quot;${MONGO_PASSWORD}&quot;
    expose:
      - &quot;27017&quot;
    volumes:
      - type: &quot;volume&quot;
        target: &quot;/data/db&quot;
        source: &quot;aspire-starter.apphost-06ac3d63aa-mongo-data&quot;
        read_only: false
    networks:
      - &quot;aspire&quot;
  apiservice:
    image: &quot;${APISERVICE_IMAGE}&quot;
    environment:
      OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: &quot;true&quot;
      OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: &quot;true&quot;
      OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: &quot;in_memory&quot;
      ASPNETCORE_FORWARDEDHEADERS_ENABLED: &quot;true&quot;
      HTTP_PORTS: &quot;${APISERVICE_PORT}&quot;
      ConnectionStrings__mongo: &quot;mongodb://admin:${MONGO_PASSWORD}@mongo:27017?authSource=admin&amp;amp;authMechanism=SCRAM-SHA-256&quot;
      OTEL_EXPORTER_OTLP_ENDPOINT: &quot;http://aspire-starter-dashboard:18889&quot;
      OTEL_EXPORTER_OTLP_PROTOCOL: &quot;grpc&quot;
      OTEL_SERVICE_NAME: &quot;apiservice&quot;
    expose:
      - &quot;${APISERVICE_PORT}&quot;
    depends_on:
      mongo:
        condition: &quot;service_started&quot;
    networks:
      - &quot;aspire&quot;
networks:
  aspire:
    driver: &quot;bridge&quot;
volumes:
  aspire-starter.apphost-06ac3d63aa-mongo-data:
    driver: &quot;local&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just to repeat, the publish and deployment aspects of Aspire are entirely optional. If you&apos;ve already got Infrastructure as Code and/or CI/CD pipelines you can continue to use those, and just use Aspire for local development. But if you haven&apos;t got them in place, Aspire may be useful to help you get started.&lt;/p&gt;
&lt;h2&gt;Learning more and getting started&lt;/h2&gt;
&lt;p&gt;Head over to the new home of Aspire at &lt;a href=&quot;https://aspire.dev&quot;&gt;https://aspire.dev&lt;/a&gt; to find more details about how Aspire works and how to get started.&lt;/p&gt;
&lt;p&gt;(There is also the old documentation site learn.microsoft.com/dotnet/aspire/, but as I understand it the plan is for that content to be gradually migrated to the new site)&lt;/p&gt;
&lt;p&gt;Oh, and finally keep an eye out for my presentation happening as part of the &apos;Community&apos; day of &lt;a href=&quot;https://www.dotnetconf.net/agenda&quot;&gt;.NET Conf 2025&lt;/a&gt; where I&apos;ll be showing how well Aspire works with non-.NET applications!&lt;/p&gt;
</content>
    <media:thumbnail url="https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl.png" width="256" height="256"/>
    <media:content medium="image" url="https://david.gardiner.net.au/_astro/aspire-logo-256.CA6LsmXl.png" width="256" height="256"/>
  </entry>
</feed>
