Skip to content

docs: add TypeScript Front Door origin naming example - #1798

Merged
Maddy Montaquila (maddymontaquila) merged 3 commits into
mainfrom
sebros/front-door-typescript-sample
Oct 5, 2026
Merged

Maddy Montaquila (maddymontaquila) merged 3 commits into
mainfrom
sebros/front-door-typescript-sample

Conversation

@sebastienros

@sebastienros Sébastien Ros (sebastienros) commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

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-blocks passed.
  • Compiled the updated page with @mdx-js/mdx and verified Twoslash hides the setup and shows only the override, without diagnostics.
  • git diff --check passed.
  • Using isolated Aspire CLI 13.6.0 and real generated SDK modules, npm run build passed in .doc-tester-workspace/frontdoor-naming/AppHost.
  • ../../frontdoor-tools/aspire publish --apphost apphost.mts --output-path ../published --non-interactive --nologo passed in the TypeScript test AppHost.
  • ../frontdoor-tools/aspire publish --apphost apphost.cs --output-path published --non-interactive --nologo passed in .doc-tester-workspace/frontdoor-csharp using the unchanged C# override.
  • Asserted both generated Front Door Bicep modules produce equivalent legacy origin names with resource-group-only hashes and the 90-character limit. No cloud resources were deployed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

@aspire-repo-bot

Copy link
Copy Markdown
Contributor

Frontend HTML artifact ready

The latest frontend build uploaded the frontend-dist artifact for PR #1798. Use the VS Code button below to open this PR with GitHub Artifacts Explorer and browse the built HTML locally.

VS Code: Open PR #1798 artifacts

This comment updates automatically when a new frontend build artifact is uploaded.

@IEvangelist David Pine (IEvangelist) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.dev main):
  • 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.mts without a compute environment, so aspire publish still 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-https

Both 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 build

Output:

> 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, and aspire publish --non-interactive against 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.Cdn

But 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-frontdoor

Suggestion: 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-https

Suggestion: 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:
    1. TypeScript
    2. C#
  • The TypeScript tab was selected by default.
  • Clicking the C# tab changed the page URL to ?aspire-lang=csharp, set local storage, and synchronized other aspire-lang tab 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.Cdn completed successfully.
  • aspire add azure-frontdoor completed 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 --help lists 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 publish or the relevant deployment artifact workflow.

Recommendations

  1. Priority fixes: Fix the TypeScript sample so it compiles; the documented fallback currently blocks npm run build, aspire run, and aspire publish.
  2. Documentation gaps: Add prerequisites for aspire add azure-frontdoor, clarify ../Api/Api.csproj, and explain “experimental.”
  3. Validation: After correcting the sample, validate generated Bicep with aspire publish --non-interactive and check that the origin name expression 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, version 13.6.0+56f3e9c0d216c0c7069dabb49dd0464e4827744f
  • Cleanup confirmation: Browser closed; dev server PIDs stopped; port 4321 free; .doc-tester-workspace\ contents removed; .playwright-cli\ removed; portable CLI folder removed; git status --porcelain clean.

Comment thread src/frontend/src/content/docs/whats-new/aspire-13-6.mdx Outdated
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>
Comment thread src/frontend/src/content/docs/whats-new/aspire-13-6.mdx Outdated
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>
@maddymontaquila
Maddy Montaquila (maddymontaquila) merged commit 673b17b into main Oct 5, 2026
19 checks passed
@maddymontaquila
Maddy Montaquila (maddymontaquila) deleted the sebros/front-door-typescript-sample branch October 5, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants