.NET Aspire for JavaScript/TypeScript
⚠️ AUTHORIZED USE ONLY This skill is for educational purposes or authorized security assessments only. You must have explicit, written permission from the system owner before using this tool. Misuse of this tool is illegal and strictly prohibited.
Orchestrate JavaScript and TypeScript applications using .NET Aspire's AppHost. Aspire treats JS/TS apps as first-class resources alongside .NET services, containers, and cloud infrastructure — providing service discovery, telemetry, health monitoring, and deployment from a single code-first model.
Important: The AppHost is always written in C#, even when orchestrating JS/TS apps. The
Aspire.Hosting.JavaScriptNuGet package provides the hosting APIs.
Prerequisites
- •.NET SDK 10.0+ — Required for the Aspire AppHost
- •Aspire CLI installed (
aspire --version) - •Node.js 22+ with npm, yarn, or pnpm
- •NuGet package:
Aspire.Hosting.JavaScriptin your AppHost project
# Install in AppHost project dotnet add package Aspire.Hosting.JavaScript # For file-based AppHost (apphost.cs), use directive: #:package Aspire.Hosting.JavaScript@13.1.0
Resource Types
Three methods to add JS/TS apps to the AppHost:
| Method | Use Case | Entry Point |
|---|---|---|
AddViteApp | Vite-based apps (React, Vue, Svelte) | npm script (dev) |
AddNodeApp | Node.js apps with direct file entry | JavaScript file |
AddJavaScriptApp | General JS apps with npm scripts | npm script (dev) |
AddViteApp — Vite Applications
Best for React, Vue, Svelte, and other Vite-based frontends.
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddProject<Projects.Api>("api");
var frontend = builder.AddViteApp("frontend", "./packages/web")
.WithExternalHttpEndpoints()
.WithReference(api)
.WaitFor(api);
builder.Build().Run();
Defaults:
- •Runs
devscript during development - •Runs
buildscript when publishing - •Auto-generates Dockerfile for production
- •Auto-detects package manager from lockfiles
AddNodeApp — Node.js Applications
For Node.js servers with a direct JavaScript entry point.
var api = builder.AddNodeApp("api", "./packages/api", "src/server.js")
.WithNpm()
.WithHttpEndpoint(port: 3000, env: "PORT")
.WithHttpHealthCheck("/health");
Parameters:
- •
name— Resource name in Aspire dashboard - •
appDirectory— Path to directory containing the app - •
scriptPath— Path to the JS file to run (relative to appDirectory)
AddJavaScriptApp — Generic JavaScript
For any JS app using npm scripts. Foundation for both AddViteApp and AddNodeApp.
var app = builder.AddJavaScriptApp("app", "./frontend")
.WithHttpEndpoint(port: 3000, env: "PORT");
Defaults: Uses npm, runs dev script in development, build script when publishing.
Package Managers
npm (Default)
var app = builder.AddViteApp("frontend", "./frontend");
// npm is used automatically
// Customize npm behavior:
var app2 = builder.AddViteApp("frontend", "./frontend")
.WithNpm(installCommand: "ci", installArgs: ["--legacy-peer-deps"]);
yarn
var app = builder.AddViteApp("frontend", "./frontend")
.WithYarn();
// With custom args:
var app2 = builder.AddViteApp("frontend", "./frontend")
.WithYarn(installArgs: ["--immutable"]);
pnpm
var app = builder.AddViteApp("frontend", "./frontend")
.WithPnpm();
// With custom args:
var app2 = builder.AddViteApp("frontend", "./frontend")
.WithPnpm(installArgs: ["--frozen-lockfile"]);
Auto-install: All package managers install dependencies automatically before running. In publish mode, deterministic install commands are used (npm ci, yarn install --immutable, pnpm install --frozen-lockfile) when lockfiles exist.
Service Discovery
When you call .WithReference(api), Aspire injects environment variables into the JS app:
var api = builder.AddNodeApp("api", "./api", "server.js")
.WithNpm()
.WithHttpEndpoint(port: 3000, env: "PORT");
var frontend = builder.AddViteApp("frontend", "./frontend")
.WithReference(api) // Injects API_HTTP and API_HTTPS env vars
.WaitFor(api);
Consuming in JavaScript/TypeScript
// Access injected service URLs const apiUrl = process.env.API_HTTP || process.env.API_HTTPS; // For databases and other resources const redisHost = process.env.CACHE_HOST; const redisPort = process.env.CACHE_PORT; const pgConnectionString = process.env.POSTGRESDB_CONNECTIONSTRING;
Environment variable naming: {RESOURCE_NAME}_{PROPERTY} (uppercase, underscores).
| Resource Type | Variables Injected |
|---|---|
| HTTP service | {NAME}_HTTP, {NAME}_HTTPS |
| Redis | {NAME}_HOST, {NAME}_PORT, {NAME}_URI |
| PostgreSQL DB | {NAME}_HOST, {NAME}_PORT, {NAME}_URI, {NAME}_DATABASENAME |
| Connection string | ConnectionStrings__{name} |
Endpoints & Networking
Configure HTTP endpoint
var app = builder.AddViteApp("frontend", "./frontend")
.WithHttpEndpoint(port: 3000, env: "PORT");
- •
port— Host port (exposed to browser/other services) - •
env— Environment variable name set to the internal target port - •Aspire inserts a reverse proxy between host port and app port
External endpoints
Mark endpoints as externally accessible (browser-facing):
var frontend = builder.AddViteApp("frontend", "./frontend")
.WithExternalHttpEndpoints();
HTTPS with developer certificate (Vite)
var app = builder.AddViteApp("frontend", "./frontend")
.WithHttpsEndpoint(env: "PORT")
.WithHttpsDeveloperCertificate();
Aspire auto-injects HTTPS config into Vite without modifying vite.config.js.
Scripts & Arguments
Custom run/build scripts
var app = builder.AddJavaScriptApp("app", "./app")
.WithRunScript("start") // Use "npm run start" instead of "dev"
.WithBuildScript("prod"); // Use "npm run prod" instead of "build"
Pass arguments
// Option 1: WithArgs
var app = builder.AddViteApp("frontend", "./frontend")
.WithArgs("--no-open");
// Option 2: Custom script in package.json
// package.json: { "scripts": { "dev:no-open": "vite --no-open" } }
var app2 = builder.AddViteApp("frontend", "./frontend")
.WithRunScript("dev:no-open");
Custom Vite config file
var app = builder.AddViteApp("frontend", "./frontend")
.WithViteConfig("./vite.production.config.js");
OpenTelemetry for Node.js
Configure telemetry so JS/TS apps report to the Aspire dashboard.
Install packages
npm install @opentelemetry/api @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-trace-otlp-grpc \ @opentelemetry/exporter-metrics-otlp-grpc
Create telemetry.js (must be imported first)
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-grpc');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-grpc');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const { ATTR_SERVICE_NAME } = require('@opentelemetry/semantic-conventions');
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4317';
const sdk = new NodeSDK({
resource: resourceFromAttributes({ [ATTR_SERVICE_NAME]: 'frontend' }),
traceExporter: new OTLPTraceExporter({ url: otlpEndpoint }),
metricReader: new PeriodicExportingMetricReader({
exporter: new OTLPMetricExporter({ url: otlpEndpoint }),
}),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Import first in app entry point
require('./telemetry'); // Must be first!
const express = require('express');
// ... rest of app
Aspire auto-sets OTEL_EXPORTER_OTLP_ENDPOINT for orchestrated resources.
Integrations (Redis, PostgreSQL, etc.)
Redis
// AppHost
var cache = builder.AddRedis("cache");
var app = builder.AddNodeApp("api", "./api", "server.js")
.WithNpm()
.WithReference(cache); // Injects CACHE_HOST, CACHE_PORT, CACHE_URI
// Node.js
const redis = require('redis');
const client = redis.createClient({
socket: { host: process.env.CACHE_HOST, port: process.env.CACHE_PORT }
});
PostgreSQL
// AppHost
var postgres = builder.AddAzurePostgresFlexibleServer("postgres")
.RunAsContainer();
var db = postgres.AddDatabase("mydb");
var app = builder.AddNodeApp("api", "./api", "server.js")
.WithNpm()
.WithReference(db); // Injects MYDB_HOST, MYDB_PORT, MYDB_URI, etc.
// Node.js
const { Client } = require('pg');
const client = new Client({
host: process.env.MYDB_HOST,
port: process.env.MYDB_PORT,
database: process.env.MYDB_DATABASENAME,
});
Polyglot Patterns
Mix JS/TS apps with .NET, Python, and containers in one AppHost:
var builder = DistributedApplication.CreateBuilder(args);
// Infrastructure
var cache = builder.AddRedis("cache");
var db = builder.AddPostgres("postgres").AddDatabase("appdb");
// .NET API
var api = builder.AddProject<Projects.Api>("api")
.WithReference(db)
.WithReference(cache);
// Node.js microservice
var worker = builder.AddNodeApp("worker", "./worker", "index.js")
.WithNpm()
.WithReference(cache);
// React frontend
var frontend = builder.AddViteApp("frontend", "./frontend")
.WithExternalHttpEndpoints()
.WithReference(api)
.WaitFor(api);
builder.Build().Run();
Deployment
aspire run (Development)
aspire run
JS/TS apps run as Node.js processes (not containerized). Aspire dashboard launches with logs, traces, and metrics.
aspire deploy (Containerized)
aspire deploy
Aspire auto-generates Dockerfiles, builds container images, and deploys. JS apps become Docker containers.
aspire publish (CI/CD artifacts)
aspire publish --output-path ./aspire-output
Generates docker-compose.yaml and .env files for deployment.
Docker Compose output
Aspire generates production-ready Docker Compose for JS/TS apps:
- •Auto-generated multi-stage Dockerfile
- •Deterministic dependency install from lockfiles
- •Build step runs production build script
- •Proper environment variable injection
Common Patterns
Health checks
var api = builder.AddNodeApp("api", "./api", "server.js")
.WithNpm()
.WithHttpHealthCheck("/health");
Persistent containers
var redis = builder.AddRedis("cache")
.WithLifetime(ContainerLifetime.Persistent);
Wait for dependencies
var frontend = builder.AddViteApp("frontend", "./frontend")
.WithReference(api)
.WaitFor(api) // Wait for API to be ready
.WaitFor(cache); // Wait for Redis to be ready
Environment variables
var app = builder.AddViteApp("frontend", "./frontend")
.WithEnvironment("NODE_ENV", "development")
.WithEnvironment("VITE_API_URL", api.GetEndpoint("http"));
Quick Reference
| Task | Code |
|---|---|
| Add Vite app | builder.AddViteApp("name", "./path") |
| Add Node.js app | builder.AddNodeApp("name", "./path", "server.js") |
| Use yarn | .WithYarn() |
| Use pnpm | .WithPnpm() |
| Set port via env | .WithHttpEndpoint(port: 3000, env: "PORT") |
| Reference service | .WithReference(otherResource) |
| Wait for dependency | .WaitFor(otherResource) |
| External endpoint | .WithExternalHttpEndpoints() |
| Health check | .WithHttpHealthCheck("/health") |
| Custom script | .WithRunScript("start") |
| Pass args | .WithArgs("--no-open") |
| HTTPS dev cert | .WithHttpsDeveloperCertificate() |
| Custom Vite config | .WithViteConfig("./custom.config.js") |
References
- •Aspire JavaScript Integration
- •Aspire AppHost
- •Aspire Service Discovery
- •Aspire Dashboard
- •Aspire CLI
When to Use
This skill is applicable to execute the workflow or actions described in the overview.