Skip to content

Commit d1124d4

Browse files
committed
feat: add first .NET Aspirations article
1 parent 99fcf01 commit d1124d4

File tree

7 files changed

+206
-0
lines changed

7 files changed

+206
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
---
2+
title: ".NET Aspirations - Tailor It To Your Stack"
3+
lead: "Using .NET Aspire with a Nuxt front end"
4+
date: 2025-03-02
5+
image:
6+
src: /images/aspire_1.webp
7+
badge:
8+
label: Development
9+
tags:
10+
- .NET Aspire
11+
- ASP.NET Core
12+
- Nuxt
13+
- .NET
14+
---
15+
You might have already seen blog posts and videos showing how .NET Aspire can enhance our local development environment, using an example with a Blazor SPA and an ASP.NET Core API. However, not everyone uses Blazor; many prefer a JavaScript framework for front-end development.
16+
17+
Fortunately, we can still benefit from .NET Aspire regardless of our stack. In this article, we will explore how to use .NET Aspire to develop an application composed of an ASP.NET Core back end and a Nuxt front end.
18+
19+
## The context
20+
21+
The context is very basic: developing a web application with an ASP.NET Core API for the back end and a Nuxt application for the front end. The Nuxt application calls the ASP.NET Core API to get data to show on the website.
22+
23+
The goal is to easily run the back end and front end together locally. While using .NET Aspire is not necessary for this, .NET Aspire orchestration can simplify the process and help address some common issues.
24+
25+
## What problems are we trying to solve?
26+
27+
1. No “F5 experience” ➡️ you have to start everything separately instead of just pressing "F5" to run everything at once.
28+
29+
2. Logs are available in the separate terminals where you started the back end and front end, not in a single location
30+
31+
3. To call the back end from the front end, we have to hard-code the API URL in the front-end configuration
32+
33+
34+
## Set up the application
35+
36+
We can use this [application](https://github.com/TechWatching/AspnetWithNuxt/tree/initial-without-aspire) I created to showcase how to integrate ASP.NET Core with Nuxt in a previous [article](https://techwatching.dev/posts/aspnetcore-with-nuxt). It uses the ASP.NET Core Web API template and the Nuxt 3 with v4 compact template with very few modifications so it’s very basic: a front displaying weather forecasts retrieved from the API. You can clone the [GitHub repository](https://github.com/TechWatching/aspnetwithnuxt) (use tag [initial-without-aspire](https://github.com/TechWatching/AspnetWithNuxt/tree/initial-without-aspire)) or start from scratch following the step-by-step guide from this [article](https://techwatching.dev/posts/aspnetcore-with-nuxt).
37+
38+
![A table showing dates with weather summaries and temperatures in Celsius and Fahrenheit.](/posts/images/69.temperatures.png){.rounded-lg.mx-auto width="400"}
39+
40+
## Add .NET Aspire to our existing project
41+
42+
Since .NET 9, we no longer need to install a specific workload for .NET Aspire. We only need to install the .NET Aspire templates, as they make it easier to add .NET Aspire to our application.
43+
44+
```powershell
45+
dotnet new install Aspire.ProjectTemplates
46+
```
47+
48+
Now, we can create a new [app host project](https://learn.microsoft.com/en-us/dotnet/aspire/fundamentals/app-host-overview?tabs=docker#app-host-project) to orchestrate the different parts of our application (the `WebApp` in Nuxt.js and the `WebApi`in ASP.NET Core).
49+
50+
```powershell
51+
dotnet new aspire-apphost -o AppHost
52+
dotnet sln AspnetWithNuxt.slnx add AppHost\AppHost.csproj
53+
```
54+
55+
At the moment, the `Program.cs` file of the app host project doesn't contain much, so we need to modify it to declare two resources (one for the `WebApi`and one for `WebApp`). This way, when executing the app host, it will handle running both of them.
56+
57+
### For the `WebApi`
58+
59+
Since the `WebApi` project is in .NET and part of the same solution as the app host, it's quite straightforward:
60+
61+
1. We add the `WebApi` as a project reference in the app host project
62+
63+
```bash
64+
dotnet add .\AppHost\AppHost.csproj reference .\WebApi\WebApi.csproj
65+
```
66+
67+
This will trigger a source generator that will create a class representing the `WebApi`project by its name with its path.
68+
69+
```csharp
70+
// <auto-generated/>
71+
72+
namespace Projects;
73+
74+
[global::System.CodeDom.Compiler.GeneratedCode("Aspire.Hosting", null)]
75+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage(Justification = "Generated code.")]
76+
[global::System.Diagnostics.DebuggerDisplay("Type = {GetType().Name,nq}, ProjectPath = {ProjectPath}")]
77+
public class WebApi : global::Aspire.Hosting.IProjectMetadata
78+
{
79+
public string ProjectPath => """D:\Learning\ASP.NET\AspnetWithNuxt\WebApi\WebApi.csproj""";
80+
}
81+
```
82+
83+
2. We declare the project resource `WebApi` in the `Program.cs` file of the app host with the following code
84+
85+
86+
```csharp [AppHost/Program.cs]
87+
var webApi = builder.AddProject<Projects.WebApi>("WebApi");
88+
```
89+
90+
Thanks to the source generated `WebApi` class in the `Projects` namespace, we don’t have to specify its path.
91+
92+
### For the `WebApp`
93+
94+
We can use an integration from the [Aspire Community toolkit](https://learn.microsoft.com/en-us/dotnet/aspire/community-toolkit/overview) which contains community-driven integrations for .NET Aspire, including one for Vite applications like my Nuxt project.
95+
96+
```bash
97+
dotnet add AppHost\AppHost.csproj package CommunityToolkit.Aspire.Hosting.NodeJS.Extensions
98+
```
99+
100+
We can declare the Vite App resource in the `Program.cs` file of the app host by specifying the path of the Nuxt app:
101+
102+
```csharp [AppHost/Program.cs]
103+
var webApp= builder.AddViteApp("WebApp", "../WebApp", "pnpm")
104+
.WithPnpmPackageInstallation()
105+
.WaitFor(webApi);
106+
```
107+
108+
* `AddViteApp` let us specify the package manager we want to use, `pnpm` here
109+
110+
* `WithPnpmPackageInstallation` ensures dependencies are already installed before starting the app
111+
112+
* `WaitFor` ensures the `WebApi` is running before starting the `WebApp`
113+
114+
The only downside of using `AddViteApp` is that it doesn't currently support exposing the HTTPS endpoint. So, even though the `WebApp` will be launched by the `AppHost` and will work correctly with HTTPS, its URL in the .NET Aspire dashboard will be in HTTP. This isn't a major issue; you could just replace the `http` in the URL by `https`. But let’s fix that anyway to use another method `AddPnpmApp` (there are other methods like `AddNpmApp` for other package manager) of the Aspire Community toolkit instead:
115+
116+
```csharp [AppHost/Program.cs]
117+
var webApp= builder.AddPnpmApp("WebApp", "../WebApp", "dev")
118+
.WithHttpsEndpoint(env: "PORT")
119+
.WithExternalHttpEndpoints()
120+
.WithPnpmPackageInstallation();
121+
```
122+
123+
::callout{icon="i-heroicons-chat-bubble-left-20-solid"}
124+
You don’t need to know exactly what `AddPnpmApp` does behind the scenes, but it might be helpful to check out (use your IDE to access the decompiled code with F12). I’ve heard people like Scott Hanselman and Mark Russinovich say that we should try to “understand how things work one level below the level we are operating on”, and I find this very true. In the end, the `PnpmAdd` resource is just an executable resource called with specific commands and parameters.
125+
::
126+
127+
## Running the `AppHost` with the .NET Aspire dashboard
128+
129+
If we run the `AppHost` now, it will start the `WebApp` and `WebApi`.
130+
131+
```bash
132+
dotnet run --project AppHost/AppHost.csproj
133+
```
134+
135+
The `AppHost` also launches the .NET Aspire dashboard where we can see the resources (here the `WebApp` and the `WebApi`) with their status and details about them like the endpoints and the environment variables.
136+
137+
![Screenshot of the resources tab on the .NET Aspire dashboard.](/posts/images/69.dashboard_1.png){.rounded-lg.mx-auto}
138+
139+
There is also a `console` tab in the dashboard to visualize the console logs of the different resources.
140+
141+
![Console logs showing the execution the WebApp in the Aspire dashboard.](/posts/images/69.dashboard_2.png){.rounded-lg.mx-auto}
142+
143+
## Remove the hard-coded URL for the API
144+
145+
Everything is working properly, but the API URL is still hard-coded in the `WebApp` Nuxt configuration. To change that we will first need to add in the `WebApp` resource a reference to the `WebApi` resource.
146+
147+
```csharp [AppHost/Program.cs]
148+
var webApp= builder.AddPnpmApp("WebApp", "../WebApp", "dev")
149+
.WithHttpsEndpoint(env: "PORT")
150+
.WithExternalHttpEndpoints()
151+
.WithPnpmPackageInstallation()
152+
.WithReference(webApi)
153+
.WaitFor(webApi);
154+
```
155+
156+
.NET Aspire includes built-in support for service discovery, which automatically set the correct environment variables and connection strings when referencing resources properly.
157+
158+
![Screenshot of the environment variables in .NET Aspire dashboard for the WebApp resource.](/posts/images/69.dashboard_3.png){.rounded-lg.mx-auto}
159+
160+
Here, we can see that the endpoints of the `WebApi` have been automatically injected in the environment variables of the `WebApp`.
161+
162+
We can now get this environment variable using `process.env.services__WebApi__https__0` or `import.meta.env.services__WebApi__https__0` in the `nuxtconfig.ts` file:
163+
164+
```typescript [nuxt.config.ts]
165+
$development: {
166+
routeRules: {
167+
'/api/**': {
168+
proxy: `${import.meta.env.services__WebApi__https__0}/**`,
169+
}
170+
},
171+
```
172+
173+
That’s great but that’s not a very explicit name. Let’s define another environment variable `ApiUrl` that will contain the url of the `WebApi` HTTPS endpoint and make it available to the `WebApp`:
174+
175+
```csharp [AppHost/Program.cs]
176+
var webApp= builder.AddPnpmApp("WebApp", "../WebApp", "dev")
177+
.WithHttpsEndpoint(env: "PORT")
178+
.WithExternalHttpEndpoints()
179+
.WithPnpmPackageInstallation()
180+
.WithReference(webApi)
181+
.WaitFor(webApi)
182+
.WithEnvironment("ApiUrl", webApi.GetEndpoint("https"));
183+
```
184+
185+
The route rules configuration in the `nuxt.config.ts` files becomes the following:
186+
187+
```typescript [nuxt.config.ts]
188+
$development: {
189+
routeRules: {
190+
'/api/**': {
191+
proxy: `${import.meta.env.ApiUrl}/**`,
192+
}
193+
},
194+
```
195+
196+
And everything keep working fine.
197+
198+
![Screenshot of the .NET Aspire dashboard showing the environment variable ApiUrl on the WebApp resource.](/posts/images/69.dashboard_4.png){.rounded-lg.mx-auto}
199+
200+
## Final thoughts
201+
202+
We have seen how adding .NET Aspire to our project helped us solve the three problems we had: the lack of a unified start process, the need for a centralized place to view logs, and the possibility to remove the hard-coded API URL of our Nuxt configuration. The code for this article is available [here](https://github.com/TechWatching/AspnetWithNuxt/tree/71d2da1a2a1237a63e9a124c9d1b6b9bba508a42).
203+
204+
Since .NET Aspire is highly customizable, it's easy to adjust it to fit our stack and needs. We focused on orchestrating the two resources we had but did not configure open telemetry. We'll discuss that in a future blog post.
205+
206+
In the meantime, keep learning!

public/images/aspire_1.webp

18.1 KB
Binary file not shown.
212 KB
Loading
216 KB
Loading
87 KB
Loading
84.6 KB
Loading
7.33 KB
Loading

0 commit comments

Comments
 (0)