Host external executables in Aspire
Ce contenu n’est pas encore disponible dans votre langue.
In Aspire, you can host external executable applications alongside your projects using the AddExecutable method. This capability is useful when you need to integrate executable applications or tools into your distributed application, such as Node.js applications, Python scripts, or specialized CLI tools.
When to use executable resources
Section titled “When to use executable resources”Use executable resources when you need to:
- Run applications or tools directly on the host instead of in a container.
- Integrate command-line tools or utilities into your application.
- Run external processes that other resources depend on.
- Develop with tools that provide local development servers.
Common examples include:
- Frontend development servers: Tools like Vercel CLI or webpack dev server.
- Language-specific applications: Node.js apps, Python scripts, or Go applications.
- Database tools: Migration utilities or database seeders.
- Build tools: Asset processors or code generators.
Basic usage
Section titled “Basic usage”The AddExecutable method requires a resource name, the executable path, and optionally command-line arguments and a working directory:
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
// Basic executable without argumentsconst nodeApp = await builder.addExecutable("frontend", "node", ".", ["server.js"]);
// Executable with command-line argumentsconst pythonApp = await builder.addExecutable("api", "python", ".", ["-m", "uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000"]);
await builder.build().run();var builder = DistributedApplication.CreateBuilder(args);
// Basic executable without argumentsvar nodeApp = builder.AddExecutable("frontend", "node", ".", "server.js");
// Executable with command-line argumentsvar pythonApp = builder.AddExecutable( "api", "python", ".", "-m", "uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000");
builder.Build().Run();This code demonstrates setting up a basic executable resource. The first example runs a Node.js server script, while the second starts a Python application using Uvicorn with specific configuration options passed as arguments directly to the AddExecutable method.
Resource dependencies and environment configuration
Section titled “Resource dependencies and environment configuration”You can provide command-line arguments directly in the AddExecutable call and configure environment variables for resource dependencies. Executable resources can reference other resources and access their connection information.
Arguments in the AddExecutable call
Section titled “Arguments in the AddExecutable call”import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
// Arguments provided directly in addExecutableconst const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("vercel-dev", "vercel", ".", ["dev", "--listen", "3000"]);var builder = DistributedApplication.CreateBuilder(args);
// Arguments provided directly in AddExecutablevar app = builder.AddExecutable( "vercel-dev", "vercel", ".", "dev", "--listen", "3000");Resource dependencies with environment variables
Section titled “Resource dependencies with environment variables”For arguments that depend on other resources, use environment variables:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const redis: RedisResource
redis = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addRedis(name: string, options?: { port?: number; password?: string | ParameterResource;}): RedisResource (+1 overload)
Adds a Redis container to the application model.
addRedis("cache");const const postgres: PostgresDatabaseResource
postgres = (await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addPostgres(name: string, options?: { userName?: string | ParameterResource; password?: string | ParameterResource; port?: number;}): PostgresServerResource (+1 overload)
Adds a PostgreSQL resource to the application model. A container is used for local development.
addPostgres("postgres")).PostgresServerResource.addDatabase(name: string, options?: { databaseName?: string;} | undefined): PostgresDatabaseResource (+1 overload)
Adds a PostgreSQL database to the application model.
addDatabase("appdb");
const const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("worker", "python", ".", ["worker.py"]) .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): ExecutableResource (+1 overload)
Adds a reference to another resource
withReference(const redis: RedisResource
redis) // Provides ConnectionStrings__cache .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): ExecutableResource (+1 overload)
Adds a reference to another resource
withReference(const postgres: PostgresDatabaseResource
postgres); // Provides ConnectionStrings__appdbvar builder = DistributedApplication.CreateBuilder(args);
var redis = builder.AddRedis("cache");var postgres = builder.AddPostgres("postgres").AddDatabase("appdb");
var app = builder.AddExecutable("worker", "python", ".", "worker.py") .WithReference(redis) // Provides ConnectionStrings__cache .WithReference(postgres); // Provides ConnectionStrings__appdbWhen one resource depends on another, WithReference passes along environment variables containing the dependent resource’s connection details. For example, the worker executable’s reference to redis and postgres provides it with the ConnectionStrings__cache and ConnectionStrings__appdb environment variables, which contain connection strings to these resources.
Access specific endpoint information
Section titled “Access specific endpoint information”For more control over how connection information is passed to your executable:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder, type EndpointProperty = "Url" | "Host" | "IPV4Host" | "Port" | "Scheme" | "TargetPort" | "HostAndPort" | "TlsEnabled"const EndpointProperty: { readonly Url: "Url"; readonly Host: "Host"; readonly IPV4Host: "IPV4Host"; readonly Port: "Port"; readonly Scheme: "Scheme"; readonly TargetPort: "TargetPort"; readonly HostAndPort: "HostAndPort"; readonly TlsEnabled: "TlsEnabled";}
Enum Aspire.Hosting.ApplicationModel.EndpointProperty
EndpointProperty } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const redis: RedisResource
redis = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addRedis(name: string, options?: { port?: number; password?: string | ParameterResource;}): RedisResource (+1 overload)
Adds a Redis container to the application model.
addRedis("cache");const const redisEndpoint: EndpointReference
redisEndpoint = await const redis: RedisResource
redis.ContainerResource.getEndpoint(name: string): EndpointReference
Gets an endpoint reference
getEndpoint("tcp");const const redisHost: EndpointReferenceExpression
redisHost = await const redisEndpoint: EndpointReference
redisEndpoint.EndpointReference.property(property: EndpointProperty): EndpointReferenceExpression
Gets the specified property expression of the endpoint.
property(const EndpointProperty: { readonly Url: "Url"; readonly Host: "Host"; readonly IPV4Host: "IPV4Host"; readonly Port: "Port"; readonly Scheme: "Scheme"; readonly TargetPort: "TargetPort"; readonly HostAndPort: "HostAndPort"; readonly TlsEnabled: "TlsEnabled";}
Enum Aspire.Hosting.ApplicationModel.EndpointProperty
EndpointProperty.type Host: "Host"
Host);const const redisPort: EndpointReferenceExpression
redisPort = await const redisEndpoint: EndpointReference
redisEndpoint.EndpointReference.property(property: EndpointProperty): EndpointReferenceExpression
Gets the specified property expression of the endpoint.
property(const EndpointProperty: { readonly Url: "Url"; readonly Host: "Host"; readonly IPV4Host: "IPV4Host"; readonly Port: "Port"; readonly Scheme: "Scheme"; readonly TargetPort: "TargetPort"; readonly HostAndPort: "HostAndPort"; readonly TlsEnabled: "TlsEnabled";}
Enum Aspire.Hosting.ApplicationModel.EndpointProperty
EndpointProperty.type Port: "Port"
Port);
const const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("app", "node", ".", ["app.js"]) .ExecutableResource.withReference(source: EndpointReference | string | uri, options?: { connectionName?: string; optional?: boolean; name?: string;} | undefined): ExecutableResource (+1 overload)
Adds a reference to another resource
withReference(const redis: RedisResource
redis) .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("REDIS_HOST", const redisHost: EndpointReferenceExpression
redisHost) .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("REDIS_PORT", const redisPort: EndpointReferenceExpression
redisPort);var builder = DistributedApplication.CreateBuilder(args);
var redis = builder.AddRedis("cache");
var app = builder.AddExecutable("app", "node", ".", "app.js") .WithReference(redis) .WithEnvironment(context => { // Provide individual connection details context.EnvironmentVariables["REDIS_HOST"] = redis.Resource.PrimaryEndpoint.Property(EndpointProperty.Host); context.EnvironmentVariables["REDIS_PORT"] = redis.Resource.PrimaryEndpoint.Property(EndpointProperty.Port); });Practical example: Vercel CLI
Section titled “Practical example: Vercel CLI”Here’s a complete example using the Vercel CLI to host a frontend application with a backend API:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
// Backend APIconst const api: ProjectResource
api = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addProject(name: string, projectPath: string, options?: { launchProfileOrOptions?: ProjectResourceOptions;}): ProjectResource (+1 overload)
Adds a .NET project resource
addProject("api", "./Api/Api.csproj") .ProjectResource.withExternalHttpEndpoints(): ProjectResource
Marks existing http or https endpoints on a resource as external.
withExternalHttpEndpoints();
// Frontend with Vercel CLIconst const frontend: ExecutableResource
frontend = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("vercel-dev", "vercel", ".", ["dev", "--listen", "3000"]) .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("API_URL", const api: ProjectResource
api.ProjectResource.getEndpoint(name: string): EndpointReference
Gets an endpoint reference
getEndpoint("http")) .ExecutableResource.withHttpEndpoint(options?: { port?: number; targetPort?: number; name?: string; env?: string; isProxied?: boolean;} | undefined): ExecutableResource (+1 overload)
Adds an HTTP endpoint
withHttpEndpoint({ port?: number | undefined
port: 3000, name?: string | undefined
name: "http" });
await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.build(): DistributedApplication
Builds the distributed application
build().DistributedApplication.run(cancellationToken?: cancellationToken): void
Runs the distributed application
run();var builder = DistributedApplication.CreateBuilder(args);
// Backend APIvar api = builder.AddProject<Projects.Api>("api") .WithExternalHttpEndpoints();
// Frontend with Vercel CLIvar frontend = builder.AddExecutable( "vercel-dev", "vercel", ".", "dev", "--listen", "3000") .WithEnvironment("API_URL", api.GetEndpoint("http")) .WithHttpEndpoint(port: 3000, name: "http");
builder.Build().Run();Configure endpoints
Section titled “Configure endpoints”Executable resources can expose HTTP endpoints that other resources can reference:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const frontend: ExecutableResource
frontend = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("webpack-dev", "npx", ".", ["webpack", "serve", "--port", "8080", "--host", "0.0.0.0"]) .ExecutableResource.withHttpEndpoint(options?: { port?: number; targetPort?: number; name?: string; env?: string; isProxied?: boolean;} | undefined): ExecutableResource (+1 overload)
Adds an HTTP endpoint
withHttpEndpoint({ port?: number | undefined
port: 8080, name?: string | undefined
name: "http" });
// Another service can reference the frontendconst const e2eTests: ExecutableResource
e2eTests = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("playwright", "npx", ".", ["playwright", "test"]) .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("BASE_URL", const frontend: ExecutableResource
frontend.ExecutableResource.getEndpoint(name: string): EndpointReference
Gets an endpoint reference
getEndpoint("http"));var builder = DistributedApplication.CreateBuilder(args);
var frontend = builder.AddExecutable( "webpack-dev", "npx", ".", "webpack", "serve", "--port", "8080", "--host", "0.0.0.0") .WithHttpEndpoint(port: 8080, name: "http");
// Another service can reference the frontendvar e2eTests = builder.AddExecutable("playwright", "npx", ".", "playwright", "test") .WithEnvironment("BASE_URL", frontend.GetEndpoint("http"));Environment configuration
Section titled “Environment configuration”Configure environment variables for your executable:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("api", "uvicorn", ".", ["main:app", "--reload", "--host", "0.0.0.0"]) .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("DEBUG", "true") .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("LOG_LEVEL", "info") .ExecutableResource.withEnvironment(name: string, value: string | IResourceWithConnectionString | IValueProvider): ExecutableResource
Sets an environment variable
withEnvironment("START_TIME", new var Date: DateConstructornew () => Date (+3 overloads)
Date().Date.toISOString(): string
Returns a date as a string value in ISO format.
toISOString());var builder = DistributedApplication.CreateBuilder(args);
var app = builder.AddExecutable( "api", "uvicorn", ".", "main:app", "--reload", "--host", "0.0.0.0") .WithEnvironment("DEBUG", "true") .WithEnvironment("LOG_LEVEL", "info") .WithEnvironment(context => { // Dynamic environment variables context.EnvironmentVariables["START_TIME"] = DateTimeOffset.UtcNow.ToString(); });withEnvironment API unification in Aspire 13.3
Section titled “withEnvironment API unification in Aspire 13.3”Aspire 13.3 unified non-C# AppHost environment assignment behind a single withEnvironment(name, value) pattern. The public TypeScript API accepts plain strings, reference expressions, endpoint references, parameter resources, supported resources that expose connection strings, expression values, and awaitable forms of supported values.
When upgrading to Aspire 13.3, replace every earlier per-kind environment helper call with withEnvironment(name, value). The 13.3 TypeScript SDK doesn’t generate compatibility aliases for those helpers.
Publishing with PublishAsDockerFile
Section titled “Publishing with PublishAsDockerFile”For production deployment, executable resources need to be containerized. Use the PublishAsDockerFile method to specify how the executable should be packaged:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("frontend", "npm", ".", ["start", "--port", "3000"]) .ExecutableResource.publishAsDockerFile(configure: (obj: ContainerResource) => Promise<void>): ExecutableResource
Publishes an executable as a Docker file
publishAsDockerFile(async () => {});var builder = DistributedApplication.CreateBuilder(args);
var app = builder.AddExecutable( "frontend", "npm", ".", "start", "--port", "3000") .PublishAsDockerFile();When you call PublishAsDockerFile(), Aspire generates a Dockerfile during the publish process. You can customize this by providing your own Dockerfile:
Custom Dockerfile for publishing
Section titled “Custom Dockerfile for publishing”Create a Dockerfile in your executable’s working directory:
FROM node:22-alpineWORKDIR /appCOPY package*.json ./RUN npm ci --only=productionCOPY . .EXPOSE 3000CMD ["npm", "start"]Then reference it in your AppHost:
import { function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder } from './.aspire/modules/aspire.mjs';
const const builder: IDistributedApplicationBuilder
builder = await function createBuilder(): IDistributedApplicationBuilder
Creates a new distributed application builder
createBuilder();
const const app: ExecutableResource
app = await const builder: IDistributedApplicationBuilder
builder.IDistributedApplicationBuilder.addExecutable(name: string, command: string, workingDirectory: string, args: string[]): ExecutableResource
Adds an executable resource to the application model.
addExecutable("frontend", "npm", ".", ["start"]) .ExecutableResource.publishAsDockerFile(configure: (obj: ContainerResource) => Promise<void>): ExecutableResource
Publishes an executable as a Docker file
publishAsDockerFile(async () => {});var builder = DistributedApplication.CreateBuilder(args);
var app = builder.AddExecutable("frontend", "npm", ".", "start") .PublishAsDockerFile([new DockerfileBuildArg("NODE_ENV", "production")]);Best practices
Section titled “Best practices”When working with executable resources:
- Use explicit paths: For better reliability, use full paths to executables when possible.
- Handle dependencies: Use
WithReferenceto establish proper dependency relationships. - Configure explicit start: Use
WithExplicitStart()for executables that shouldn’t start automatically. - Prepare for deployment: Always use
PublishAsDockerFile()for production scenarios. - Environment isolation: Use environment variables rather than command-line arguments for sensitive configuration.
- Resource naming: Use descriptive names that clearly identify the executable’s purpose.