Repository navigation
docs: add TypeScript Front Door origin naming example - #1798
Maddy Montaquila (maddymontaquila) merged 3 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The sample matches the Aspire 13.6 APIs, previous naming policy, and repository language-tab conventions.
Review effort: Balanced
Findings: None
What changed in this PR
Adds TypeScript parity for the Aspire 13.6 Front Door origin-name compatibility fallback.
Changes:
- Adds synchronized TypeScript/C# AppHost tabs.
- Demonstrates typed origin lookup and legacy naming behavior.
- Documents the required experimental provisioning package.
| File | Description |
|---|---|
src/frontend/src/content/docs/whats-new/aspire-13-6.mdx |
Adds the TypeScript Front Door fallback example. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Frontend HTML artifact readyThe latest frontend build uploaded the This comment updates automatically when a new frontend build artifact is uploaded. |
David Pine (IEvangelist)
left a comment
There was a problem hiding this comment.
Docs accuracy review: changes requested
The new TypeScript fallback sample doesn't compile. In the TypeScript API, origin.bicepIdentifier is a zero-argument async method, not a { get, set } property object, so origin.bicepIdentifier.get() fails with TS2339 (see the inline comment on L922). Changing only that line to await origin.bicepIdentifier() fixes it. The build, aspire publish, and generated Bicep are validated under Fix validation below.
Summary
- Phase A sources (
microsoft/aspire; this PR targets aspire.devmain):main@8eec461e839a1e04b7b790c12e01ead5e4f48cbf: primary source.release/13.6@d6b50f5779bfdb127a74974ab034f5158d60041c: cross-check; every cited line is identical.release/13.5@9c1b401dd67746739044f68959cbf4d3d7af93a6: used only for the pre-13.6 origin-name snapshot.CommunityToolkit/AspireandAzure/azure-sdk-for-netweren't needed.
- Claims: 25 extracted (24 non-narrative, 1 narrative): 22 verified, 1 verified-with-nuance, 1 contradicted, 0 unverifiable.
- Phase B (doc-tester): exercised the target section of
/whats-new/aspire-13-6/and the linked/integrations/cloud/azure/azure-front-door/page: 1 critical (the TypeScript sample doesn't compile), 3 warnings, 4 knowledge-gap notes. - Also worth fixing: the TypeScript tab is a complete
apphost.mtswithout a compute environment, soaspire publishstill fails after the L922 fix (A11 below).
Phase A: claim verification
Contradicted (1): A18, L922 origin.bicepIdentifier.get(). The inline comment has the evidence and a one-line suggestion.
Verified with nuance (1): A11, L914 await frontdoor.withOrigin(api);
withOrigin exists as written (AzureFrontDoorExtensions.cs#L187-L190). However, the default infrastructure resolves each origin's host through that resource's compute environment (#L92-L93), and GetEffectiveComputeEnvironment throws when there isn't one. The TypeScript tab is a complete apphost.mts that never adds a compute environment, so copying it (with L922 fixed) fails at publish:
❌ Capability Error: Could not invoke 'run': resource 'api' does not have a compute environment. Ensure a compute environment (e.g., Azure Container Apps, Azure App Service) is configured in the application model.
Adding await builder.addAzureContainerAppEnvironment('env'); (after aspire add azure-appcontainers) made publish succeed. Consider adding that line, or a comment saying api must be deployed to a compute environment. The C# tab is a fragment for an existing AppHost, so it doesn't invite the same copy-paste failure.
Verified (22) and verified-with-nuance (1) claims, with evidence
Links point to microsoft/aspire main @ 8eec461e unless noted. Line numbers refer to aspire-13-6.mdx at the PR head.
| # | Line | Claim | Verdict | Evidence |
|---|---|---|---|---|
| A1 | L895 | Previous names can be preserved by overriding them in an infrastructure customization callback | verified | ConfigureInfrastructure is exported to polyglot AppHosts and appends to the resource's callback (AzureProvisioningResourceExtensions.cs#L41-L48). The integration's default naming callback is passed to the resource constructor (AzureFrontDoorExtensions.cs#L66, #L160), and the combined delegate runs in AzureProvisioningResource.cs#L83, so user callbacks run after the defaults. |
| A2 | L900 | Aspire.Hosting.Azure.Provisioning.Cdn exists and is experimental |
verified | Prerelease-only package (SuppressFinalPackageVersion) that links the shared experimental attribute (Aspire.Hosting.Azure.Provisioning.Cdn.csproj#L7-L14, ExperimentalAssemblyInfo.cs#L6: ASPIREAZUREPROVISIONING001); README.md#L4. Phase B installed 13.6.0-preview.1.26479.8. |
| A3 | L900 | The package enables typed origin lookups | verified | AtsTypeMappings.cs#L7: GenerateAspireProvisioningProxy(typeof(CdnProfile), IncludeContainingAssemblyTypes = true). |
| A4 | L903 | aspire add Aspire.Hosting.Azure.Provisioning.Cdn |
verified | README.md#L19; Phase B ran it successfully. |
| A5 | L906 | The TypeScript AppHost file is apphost.mts |
verified | DefaultLanguageDiscovery.cs#L37-L39. |
| A6 | L907 | import { createBuilder } from './.aspire/modules/aspire.mjs'; |
verified | ts-starter/apphost.mts#L1. |
| A7 | L909 | await createBuilder() |
verified | ts-starter/apphost.mts#L3. |
| A8 | L910 | builder.addProject('api', '../Api/Api.csproj') |
verified | ProjectResourceBuilderExtensions.cs#L114-L115: [AspireExport("addProject")]. |
| A9 | L911 | api.withExternalHttpEndpoints() |
verified | ResourceBuilderExtensions.cs#L1919-L1920. |
| A10 | L913 | builder.addAzureFrontDoor('frontdoor') |
verified | AzureFrontDoorExtensions.cs#L56-L57. |
| A11 | L914 | frontdoor.withOrigin(api) |
verified-with-nuance | AzureFrontDoorExtensions.cs#L187-L190. Publishing needs a compute environment for api (#L92-L93, #L207-L217); see above. |
| A12 | L915 | frontdoor.configureInfrastructure(async (infrastructure) => { ... }) |
verified | AzureProvisioningResourceExtensions.cs#L41-L42. |
| A13 | L916 | infrastructure.bicep() |
verified | BicepValueFactory.cs#L18: [AspireExport("bicep")]. |
| A14 | L917 | bicep.member(await bicep.resourceGroup(), 'id') |
verified | BicepValueFactory.cs#L353-L354, #L395-L396; same call in Aspire.Hosting.Azure/TypeScript/apphost.mts#L82. |
| A15 | L918 | bicep.uniqueString([groupId]) |
verified | BicepValueFactory.cs#L252-L253; Aspire.Hosting.Azure/TypeScript/apphost.mts#L83. |
| A16 | L920 | infrastructure.getFrontDoorOrigins() returns the origin proxies |
verified | Lookups are named "Get" + PluralizeIdentifier(typeName) and return proxy[] (AspireProvisioningProxyGenerator.cs#L1605, #L1643, #L1561, #L2315), the same pattern as getStorageAccounts() (Aspire.Hosting.Azure.Storage/TypeScript/apphost.mts#L35). The fix-validation build confirmed it. |
| A17 | L921 | The previous default removed underscores and hashed only the resource-group ID | verified | Origin snapshot on release/13.5 L40: take('myapiOrigin-${uniqueString(resourceGroup().id)}', 90). On main L40: take('my-apiOrigin-${uniqueString(resourceGroup().id, my_api_host)}', 90), from AzureFrontDoorExtensions.cs#L132-L134. |
| A19 | L923 | bicep.string(...) |
verified | BicepValueFactory.cs#L46-L47. |
| A20 | L924 | bicep.concat([prefix, suffix]) |
verified | BicepValueFactory.cs#L220-L221; Aspire.Hosting.Azure.AppService/TypeScript/apphost.mts#L40. |
| A21 | L925 | Front Door origin names have a maximum length of 90 characters | verified | Both origin snapshots end with , 90); the C# default uses origin.GetResourceNameRequirements().MaxLength (AzureFrontDoorExtensions.cs#L134). |
| A22 | L926 | origin.name.set(await bicep.take(name, 90)) |
verified | BicepValueFactory.cs#L285-L286 (take). FrontDoorOrigin.Name is settable, so it's projected as a { get, set } object (AtsTypeScriptCodeGenerator.cs#L2207); compare provisionedIdentity.name.set(...) in Aspire.Hosting.Azure/TypeScript/apphost.mts#L89. |
| A23 | L930 | await builder.build().run(); |
verified | ts-starter/apphost.mts#L20. |
| A24 | L897-L957 | The TypeScript tab is equivalent to the C# override | verified (after the L922 fix) | Fix validation emitted take(concat('apiOrigin-', uniqueString(resourceGroup().id)), 90), the same value as the C# take('<id>-${uniqueString(resourceGroup().id)}', 90). The TypeScript tab hard-codes 90; C# uses MaxLength, which is also 90. |
Narrative, not verified: L895 "before the first deployment after upgrading".
Fix validation
A source-blind sandbox run with the GA Aspire CLI 13.6.0+56f3e9c0d216c0c7069dabb49dd0464e4827744f:
aspire new aspire-ts-empty -n FrontDoorTest -o FrontDoorTest --non-interactive --nologo
aspire add azure-frontdoor --non-interactive --nologo
aspire add Aspire.Hosting.Azure.Provisioning.Cdn --non-interactive --nologo
dotnet new web -n Api -o Api --no-httpsBoth aspire add commands installed 13.6.0-preview.1.26479.8. The PR's TypeScript sample was pasted verbatim except for L922, which became const identifier = await origin.bicepIdentifier();.
| Step | Result |
|---|---|
npm run build (eslint apphost.mts && tsc -p tsconfig.apphost.json) |
✅ Exit 0 |
aspire publish --non-interactive -o <out>, with the sample as documented |
❌ Exit 6: resource 'api' does not have a compute environment (A11) |
The same, plus aspire add azure-appcontainers and await builder.addAzureContainerAppEnvironment('env'); after createBuilder() |
✅ Pipeline succeeded (11/11 steps) |
Generated frontdoor/frontdoor.bicep:
resource apiOrigin 'Microsoft.Cdn/profiles/originGroups/origins@2025-06-01' = {
name: take(concat('apiOrigin-', uniqueString(resourceGroup().id)), 90)
properties: {
hostName: api_host
originHostHeader: api_host
}
parent: apiOriginGroup
}That's the pre-13.6 name: the origin identifier without underscores, a hyphen, and a hash of only the resource-group ID. The new 13.6 default would also hash api_host. With the one-line fix, the TypeScript tab matches the C# override.
Phase B: doc-tester results (verbatim)
The PR head (c9634bec9461) was served locally at http://localhost:4321/ and tested source-blind with the doc-tester skill. The report follows unedited.
Documentation Test Report
Focus Area: PR #1798, /whats-new/aspire-13-6/, Azure Front Door origin naming fallback TypeScript/C# synchronized tabs
Date: 2026-10-01
Tester: doc-tester agent
Summary
| Category | Passed | Failed | Warnings |
|---|---|---|---|
| Content Accuracy | 4 | 1 | 3 |
| Code Examples | 3 | 1 | 1 |
| CLI Commands | 4 | 0 | 1 |
| Links | 3 | 0 | 0 |
Critical Issues
Issue 1: TypeScript fallback sample does not compile
Location: http://localhost:4321/whats-new/aspire-13-6/#front-door-origin-names-now-include-the-backend-hostname, TypeScript tab
Type: Example
Severity: Critical
What the documentation says:
“Fallback: If the recommended deployment and cleanup approach isn’t feasible, preserve the previous default origin names by overriding them in an infrastructure customization callback before the first deployment after upgrading:”
The TypeScript tab shows:
for (const origin of await infrastructure.getFrontDoorOrigins()) {
// Match the previous default: remove underscores and hash only the resource-group ID.
const identifier = await origin.bicepIdentifier.get();
const prefix = await bicep.string(`${identifier.replaceAll('_', '')}-`);
const name = await bicep.concat([prefix, suffix]);
// Front Door origin names have a maximum length of 90 characters.
await origin.name.set(await bicep.take(name, 90));
}What actually happens:
A fresh TypeScript AppHost with the documented packages cannot type-check or run the copied sample. TypeScript reports that origin.bicepIdentifier.get does not exist.
Evidence:
Commands run:
& "$env:TEMP\pr1798-review\aspire-cli\aspire.exe" new aspire-ts-empty -n FrontDoorTest -o FrontDoorTest --non-interactive --nologo
& "$env:TEMP\pr1798-review\aspire-cli\aspire.exe" add azure-frontdoor --non-interactive --nologo
& "$env:TEMP\pr1798-review\aspire-cli\aspire.exe" add Aspire.Hosting.Azure.Provisioning.Cdn --non-interactive --nologo
dotnet new web -n Api -o Api --no-https
npm run buildOutput:
> frontdoortest@1.0.0 build
> eslint apphost.mts && tsc -p tsconfig.apphost.json
apphost.mts(16,53): error TS2339: Property 'get' does not exist on type '() => Promise<string>'.
exit=2
aspire run also failed:
## aspire run --non-interactive
⚙️ Preparing Aspire server...
apphost.mts(16,53): error TS2339: Property 'get' does not exist on type '() => Promise<string>'.
❌ The project could not be built.
aspire publish also failed, so I could not inspect generated Bicep:
## aspire publish --non-interactive
🛠️ Executing step publish
apphost.mts(16,53): error TS2339: Property 'get' does not exist on type '() => Promise<string>'.
❌ An unexpected error occurred: The TypeScript (Node.js) apphost failed.
Recommended Action:
- Update the TypeScript code sample to use the actual generated API shape for
bicepIdentifier. - Re-run
npm run build,aspire run --non-interactive, andaspire publish --non-interactiveagainst the corrected sample. - If the API is intentionally experimental, add a note about API volatility and the validated Aspire/package version.
Warnings
Warning 1: The fallback section does not state all packages needed for the TypeScript sample
Location: /whats-new/aspire-13-6/#front-door-origin-names-now-include-the-backend-hostname
Issue: The fallback section says:
“Add the experimental CDN provisioning package to enable typed origin lookups:”
and shows:
aspire add Aspire.Hosting.Azure.Provisioning.CdnBut the sample also uses builder.addAzureFrontDoor('frontdoor') and frontdoor.withOrigin(api). A new user needs the Front Door hosting integration too. I only learned that from the linked Azure Front Door integration page, which says:
“To access this type and APIs, add the 📦 Aspire.Hosting.Azure.FrontDoor NuGet package to your AppHost project.”
and shows:
aspire add azure-frontdoorSuggestion: Add a short prerequisite line before the fallback tabs, for example: “This sample assumes the Azure Front Door hosting integration is already added; if not, run aspire add azure-frontdoor first.”
Warning 2: The TypeScript fallback section does not state how to create or locate ../Api/Api.csproj
Location: /whats-new/aspire-13-6/#front-door-origin-names-now-include-the-backend-hostname
Issue: The sample includes:
const api = await builder.addProject('api', '../Api/Api.csproj');The section does not explain where this project comes from. As a tester, I had to create it myself:
dotnet new web -n Api -o Api --no-httpsSuggestion: Clarify that ../Api/Api.csproj is a placeholder for an existing backend project, or add a minimal setup note for creating the referenced API project.
Warning 3: “Experimental” is mentioned but not explained
Location: /whats-new/aspire-13-6/#front-door-origin-names-now-include-the-backend-hostname
Issue: The docs say:
“Add the experimental CDN provisioning package to enable typed origin lookups:”
The page does not explain what “experimental” means for a user copying this fallback: whether APIs may change, whether warnings/diagnostics must be suppressed, or whether this should be removed later.
Observed CLI behavior:
✅ The package Aspire.Hosting.Azure.Provisioning.Cdn::13.6.0-preview.1.26479.8 was added successfully.
Suggestion: Add a brief note about the experimental status and any expected warnings or version caveats.
Passed Checks
- The page loaded at
http://localhost:4321/whats-new/aspire-13-6/. - The target heading rendered and had a working anchor:
#front-door-origin-names-now-include-the-backend-hostname- Browser check:
targetExists: true
- The section clearly explains the breaking change:
“Azure Front Door origin names generated by the Aspire hosting integration now combine the resource group ID with the origin’s backend hostname, instead of hashing the resource group ID alone.”
- The section explains the recommended path:
“Deploy your app with Aspire 13.6 using the new default origin names.”
“Confirm that the newly named origins are healthy…”
“Manually delete the old origins…” - The section explains when to use the fallback:
“If the recommended deployment and cleanup approach isn’t feasible…”
- The synchronized tab group rendered in the requested order:
TypeScriptC#
- The TypeScript tab was selected by default.
- Clicking the C# tab changed the page URL to
?aspire-lang=csharp, set local storage, and synchronized otheraspire-langtab groups:{ "aspire-lang": "csharp", "starlight-synced-tabs__aspire-lang": "C#" } - Clicking TypeScript changed the page URL to
?aspire-lang=typescript. - The TypeScript Twoslash block rendered without visible diagnostic/error CSS classes in the browser.
- Copy button metadata existed for the documented command and code blocks. For the command block, the copy target matched:
aspire add Aspire.Hosting.Azure.Provisioning.Cdn - The internal Learn More link resolved without 404:
/integrations/cloud/azure/azure-front-door/- Page title:
Azure Front Door | Aspire - H1:
Azure Front Door
- The section’s internal links were site-relative or anchors as appropriate:
#front-door-origin-names-now-include-the-backend-hostname/integrations/cloud/azure/azure-front-door/
- Browser console showed only the expected local dev-server live API issue, not a section-specific rendering error:
[ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://localhost:4321/api/live/ [WARNING] [live-status] Live API not found (404); live updates disabled until reload. aspire add Aspire.Hosting.Azure.Provisioning.Cdncompleted successfully.aspire add azure-frontdoorcompleted successfully after reading the linked Azure Front Door docs.
Knowledge Gap Notes
Knowledge Gap: Choosing a TypeScript AppHost template
What I needed to know: Which template creates a TypeScript AppHost.
Source of my knowledge: The user requested aspire new --list, but with Aspire CLI 13.6.0 it failed:
Unrecognized command or argument '--list'.
The command help listed available templates, including:
aspire-ts-empty Empty (TypeScript AppHost)
User impact: A new user following the tester workflow may be confused if they expect aspire new --list to work.
Recommendation:
- If docs mention template discovery, prefer the actual supported CLI discovery command or document that
aspire new --helplists templates.
Knowledge Gap: Creating the referenced API project
What I needed to know: How to satisfy ../Api/Api.csproj.
Source of my knowledge: Built-in/prior .NET knowledge; the tested section did not explain it.
User impact: Users without existing .NET project structure knowledge may not know how to create or substitute the backend project.
Recommendation:
- Add explanation to this page or make clear that the path is an existing backend placeholder.
Knowledge Gap: Adding the Front Door integration package
What I needed to know: The sample requires the Front Door hosting integration in addition to the CDN provisioning package.
Source of my knowledge: The linked Azure Front Door integration page, not the fallback section itself.
User impact: Users copying only the fallback section may add the provisioning package but still lack addAzureFrontDoor.
Recommendation:
- Add prerequisite text to the fallback section.
- Keep the Learn More link.
Knowledge Gap: How to validate generated Bicep
What I needed to know: Which command generates deployment artifacts without deploying.
Source of my knowledge: Built-in/prior Aspire knowledge plus the linked page’s statement:
“When you publish your app, Aspire generates Bicep alongside the manifest.”
User impact: The fallback section does not tell users how to confirm that the override reproduced previous origin names.
Recommendation:
- Add a short “To verify” sentence pointing to
aspire publishor the relevant deployment artifact workflow.
Recommendations
- Priority fixes: Fix the TypeScript sample so it compiles; the documented fallback currently blocks
npm run build,aspire run, andaspire publish. - Documentation gaps: Add prerequisites for
aspire add azure-frontdoor, clarify../Api/Api.csproj, and explain “experimental.” - Validation: After correcting the sample, validate generated Bicep with
aspire publish --non-interactiveand check that the originnameexpression matches the documented previous default naming policy.
Phase B metadata
- Routes/sections exercised:
/whats-new/aspire-13-6/#front-door-origin-names-now-include-the-backend-hostname/integrations/cloud/azure/azure-front-door/
- Local server URL:
http://localhost:4321/ - PR ref/SHA served:
pr-1798-review@c9634bec9461 - Aspire CLI used:
C:\Users\dapine\AppData\Local\Temp\pr1798-review\aspire-cli\aspire.exe, version13.6.0+56f3e9c0d216c0c7069dabb49dd0464e4827744f - Cleanup confirmation: Browser closed; dev server PIDs stopped; port
4321free;.doc-tester-workspace\contents removed;.playwright-cli\removed; portable CLI folder removed;git status --porcelainclean.
Keep TypeScript and C# as equivalent overrides for an existing AppHost. Correct the TypeScript identifier getter, document the compute environment requirement and experimental package version, and explain local Bicep verification. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep the validated package version explicit in the historical validation note while using the shared preview version for installation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Summary
The Aspire 13.6 release notes only showed the Front Door origin-name compatibility override in C#. Add the equivalent TypeScript sample in synchronized language tabs, with TypeScript first and the existing C# sample unchanged.
Both displayed samples are overrides for an existing AppHost, with shared prerequisites for the backend resource and Azure compute environment. Document the validated experimental CDN provisioning package version, its included Front Door hosting integration, and how to inspect published Bicep without deploying. The override preserves underscore removal, resource-group-only hashing, and the 90-character name limit.
Third-party links and affiliations
None.
Validation
pnpm --dir src/frontend test:unit:twoslash-blockspassed.@mdx-js/mdxand verified Twoslash hides the setup and shows only the override, without diagnostics.git diff --checkpassed.npm run buildpassed in.doc-tester-workspace/frontdoor-naming/AppHost.../../frontdoor-tools/aspire publish --apphost apphost.mts --output-path ../published --non-interactive --nologopassed in the TypeScript test AppHost.../frontdoor-tools/aspire publish --apphost apphost.cs --output-path published --non-interactive --nologopassed in.doc-tester-workspace/frontdoor-csharpusing the unchanged C# override.