Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 239 additions & 22 deletions src/frontend/src/content/docs/whats-new/aspire-13.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1655,36 +1655,140 @@ var app = builder.AddNodeApp("frontend", "../frontend", "server.js")

### Migration guide

#### Migrating from publishing callbacks to pipeline steps
This comprehensive guide helps you migrate from Aspire 9.x to 13.x, covering all breaking changes across 13.0 and 13.1 releases.

**Before (9.x)**:
#### Quick migration checklist

Follow these steps to migrate your Aspire 9.x application to 13.x:

<Steps>

1. **Update the Aspire CLI** to version 13.x:
<OsAwareTabs syncKey="terminal">
<Code slot="unix" lang="bash" code="curl -sSL https://aspire.dev/install.sh | bash" />
<Code slot="windows" lang="powershell" code='irm https://aspire.dev/install.ps1 | iex' />
</OsAwareTabs>

2. **Update your Aspire packages** using the CLI:
```bash
aspire update
```

3. **Update package references** - rename `Aspire.Hosting.NodeJs` to `Aspire.Hosting.JavaScript`

4. **Migrate JavaScript APIs** - replace `AddNpmApp()` with `AddJavaScriptApp()` and update `AddNodeApp()` signatures

5. **Migrate lifecycle hooks** - replace `IDistributedApplicationLifecycleHook` with `IDistributedApplicationEventingSubscriber`

6. **Migrate publishing callbacks** - replace `WithPublishingCallback()` with `WithPipelineStepFactory()`

7. **Update connection properties** (13.1) - rename `Model` to `ModelName` and `Database` to `DatabaseName` where applicable

8. **Update Azure Redis** (13.1) - replace `AddAzureRedisEnterprise()` with `AddAzureManagedRedis()`

9. **Review behavioral changes** - check `EndpointReference.GetValueAsync()` usage for waiting behavior

10. **Test your application** thoroughly to ensure all changes work as expected

</Steps>

#### Migrating package references

The `Aspire.Hosting.NodeJs` package has been renamed to `Aspire.Hosting.JavaScript` to reflect broader JavaScript application support.

**Update your project file:**

```xml
<!-- Before (9.x) -->
<PackageReference Include="Aspire.Hosting.NodeJs" Version="9.x.x" />

<!-- After (13.0) -->
<PackageReference Include="Aspire.Hosting.JavaScript" Version="13.0.0" />
```

**Using the CLI:**

```bash
# Before (9.x)
aspire add nodejs

# After (13.0)
aspire add javascript
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI command is shown here as aspire add javascript, but the same document’s earlier “Package renames” section shows aspire add javaScript (different casing). Please normalize the command casing throughout this page to avoid confusing readers and to match the actual CLI verb.

Suggested change
aspire add javascript
aspire add javaScript

Copilot uses AI. Check for mistakes.
```

The `aspire update` command automatically handles this migration.

#### Migrating JavaScript APIs

##### AddNpmApp → AddJavaScriptApp

The `AddNpmApp()` method has been removed. Use `AddJavaScriptApp()` with `WithRunScript()` instead:

**Before (9.x):**
```csharp
var api = builder.AddProject<Projects.Api>("api")
.WithPublishingCallback(async (context, cancellationToken) =>
{
await CustomDeployAsync(context, cancellationToken);
});
builder.AddNpmApp("frontend", "../frontend", scriptName: "dev");
```

**After (13.0)**:
**After (13.0):**
```csharp
var api = builder.AddProject<Projects.Api>("api")
.WithPipelineStepFactory(context =>
{
return new PipelineStep()
{
Name = "CustomDeployStep",
Action = CustomDeployAsync,
RequiredBySteps = [WellKnownPipelineSteps.Publish]
};
});
builder.AddJavaScriptApp("frontend", "../frontend")
.WithRunScript("dev");
```

For more details, see [Deployment pipeline documentation](/get-started/pipelines/).
<Aside type="note">
The `AddNpmApp` API supported an `args` parameter for passing command-line arguments to scripts. `AddJavaScriptApp` does not have this parameter. Instead, define custom scripts in your `package.json` with the required arguments. See [Passing arguments to scripts](#passing-arguments-to-scripts) for details.
</Aside>

##### AddNodeApp signature changes

The `AddNodeApp()` API has been refactored with a new signature that uses `appDirectory` with a relative `scriptPath`:

**Before (9.x):**
```csharp
// Absolute scriptPath with optional workingDirectory
builder.AddNodeApp(
name: "frontend",
scriptPath: "/absolute/path/to/app.js",
workingDirectory: "/absolute/path/to",
args: ["--port", "3000"]);
```

**After (13.0):**
```csharp
// appDirectory with relative scriptPath
builder.AddNodeApp(
name: "frontend",
appDirectory: "../frontend",
scriptPath: "app.js");
```

**Key changes:**

- **First parameter changed**: `scriptPath` (absolute) → `appDirectory` (relative to AppHost)
- **Second parameter changed**: `workingDirectory` (optional) → `scriptPath` (relative to appDirectory)
- **Automatic npm integration**: If `package.json` exists in `appDirectory`, npm is automatically configured
Comment on lines +1767 to +1769
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section describes the 9.x scriptPath as “absolute”, but the migration example just below uses a relative path (../frontend/server.js). Consider rewording to avoid claiming absolute paths were required (or adjust the example) so the guidance is internally consistent.

Copilot uses AI. Check for mistakes.
- **Package manager flexibility**: Use `WithNpm()`, `WithYarn()`, or `WithPnpm()` with `WithRunScript()` to execute scripts

**Migration example:**

```csharp
// Before (9.x)
var app = builder.AddNodeApp("frontend", "../frontend/server.js", "../frontend");

// After (13.0) - Option 1: Direct script execution
var app = builder.AddNodeApp("frontend", "../frontend", "server.js");

// After (13.0) - Option 2: Using package.json script
var app = builder.AddNodeApp("frontend", "../frontend", "server.js")
.WithNpm()
.WithRunScript("dev");
```

#### Migrating lifecycle hooks to events

#### Migrating from lifecycle hooks to events
Lifecycle hooks have been replaced with an eventing system that provides more flexibility and better async support.

**Before (9.x)**:
**Before (9.x):**
```csharp
public class MyLifecycleHook : IDistributedApplicationLifecycleHook
{
Expand All @@ -1699,7 +1803,7 @@ public class MyLifecycleHook : IDistributedApplicationLifecycleHook
builder.Services.TryAddLifecycleHook<MyLifecycleHook>();
```

**After (13.0)**:
**After (13.0):**
```csharp
public class MyEventSubscriber : IDistributedApplicationEventingSubscriber
{
Expand All @@ -1726,6 +1830,119 @@ public class MyEventSubscriber : IDistributedApplicationEventingSubscriber
builder.Services.TryAddEventingSubscriber<MyEventSubscriber>();
```

**Event equivalents for lifecycle hooks:**

| 9.x Lifecycle Hook | 13.0 Event |
|-------------------|-----------|
| `BeforeStartAsync()` | `BeforeStartEvent` |
| `AfterEndpointsAllocatedAsync()` | `AfterEndpointsAllocatedEvent` |
| `AfterResourcesCreatedAsync()` | `AfterResourcesCreatedEvent` |

#### Migrating publishing callbacks to pipeline steps

Publishing callbacks have been replaced with a more powerful pipeline step system through the `aspire do` command.

**Before (9.x):**
```csharp
var api = builder.AddProject<Projects.Api>("api")
.WithPublishingCallback(async (context, cancellationToken) =>
{
await CustomDeployAsync(context, cancellationToken);
});
```

**After (13.0):**
```csharp
var api = builder.AddProject<Projects.Api>("api")
.WithPipelineStepFactory(context =>
{
return new PipelineStep()
{
Name = "CustomDeployStep",
Action = CustomDeployAsync,
RequiredBySteps = [WellKnownPipelineSteps.Publish]
};
});
```

For more details, see [Deployment pipeline documentation](/get-started/pipelines/).

#### Migrating connection properties (13.1)

Aspire 13.1 renamed several connection properties for consistency across resources. If your client applications reference these properties via environment variables, update them accordingly.

| Resource | Old Property | New Property | Environment Variable Change |
|----------|-------------|--------------|---------------------------|
| GitHub Models | `Model` | `ModelName` | `{NAME}_MODEL` → `{NAME}_MODELNAME` |
| OpenAI model | `Model` | `ModelName` | `{NAME}_MODEL` → `{NAME}_MODELNAME` |
| Milvus database | `Database` | `DatabaseName` | `{NAME}_DATABASE` → `{NAME}_DATABASENAME` |
| MongoDB database | `Database` | `DatabaseName` | `{NAME}_DATABASE` → `{NAME}_DATABASENAME` |
| MySQL database | `Database` | `DatabaseName` | `{NAME}_DATABASE` → `{NAME}_DATABASENAME` |
| Oracle database | `Database` | `DatabaseName` | `{NAME}_DATABASE` → `{NAME}_DATABASENAME` |

**Example:**

```csharp
// Before (13.0) - Environment variable: CHAT_MODEL
var chat = builder.AddGitHubModels("chat");

// After (13.1) - Environment variable: CHAT_MODELNAME
// No code change needed in AppHost, but client apps must update environment variable references
var chat = builder.AddGitHubModels("chat");
```

In your client application:

```csharp
// Before (13.0)
var modelName = configuration["CHAT_MODEL"];

// After (13.1)
var modelName = configuration["CHAT_MODELNAME"];
```

#### Migrating Azure Redis (13.1)

The Azure Redis API has been renamed to align with Azure service naming conventions.

**Before (13.0):**
```csharp
var cache = builder.AddAzureRedisEnterprise("cache");
```

**After (13.1):**
```csharp
var cache = builder.AddAzureManagedRedis("cache");
```

<Aside type="note">
`AddAzureRedis()` is now obsolete. Use `AddAzureManagedRedis()` for new projects.
</Aside>

#### Behavioral changes

##### EndpointReference.GetValueAsync now waits for allocation

In Aspire 9.x, `EndpointReference.GetValueAsync()` would throw immediately if the endpoint wasn't allocated. In 13.0, it waits for allocation to complete.

**Before (9.x):**
```csharp
// Would throw immediately if not allocated
var value = await endpointRef.GetValueAsync(cancellationToken);
```

**After (13.0):**
```csharp
// Now waits for allocation - check IsAllocated if you need immediate failure
if (!endpointRef.IsAllocated)
{
throw new InvalidOperationException("Endpoint not allocated");
}
var value = await endpointRef.GetValueAsync(cancellationToken);
```

This change enables more flexible async workflows where you can wait for resources to be ready rather than polling or handling exceptions.

### Experimental features

The following features are marked as `[Experimental]` and may change in future releases:
Expand Down
Loading