Skip to main content
Distributed applications are composed of interconnected services that need to communicate with each other. .NET Aspire makes it simple to wire these dependencies using the WithReference method and the resource graph.

The Resource Graph

When you call WithReference, Aspire creates a dependency relationship between resources, forming a directed acyclic graph (DAG). This graph determines:
  • Startup order - Dependencies start before dependents
  • Service discovery - Connection information flows from dependencies to dependents
  • Environment injection - Endpoints and connection strings are automatically injected
var cache = builder.AddRedis("cache");
var db = builder.AddPostgres("postgres").AddDatabase("catalogdb");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(cache)      // api depends on cache
                 .WithReference(db);        // api depends on db
In this example:
  1. cache and postgres start first (no dependencies)
  2. catalogdb starts after postgres is ready
  3. api starts after both cache and catalogdb are ready

WithReference Basics

The WithReference method connects a resource to its dependencies:
var source = builder.AddRedis("cache");
var target = builder.AddProject<Projects.Api>("api")
                    .WithReference(source);
1
Dependency is ensured to start first
2
The source resource (cache) will start before the target resource (api).
3
Connection information is injected
4
The target resource receives environment variables with connection information for the source.
5
Service discovery is configured
6
The target can discover and connect to the source using its name.

Connection String References

For resources that provide connection strings (databases, message queues, etc.), WithReference injects a connection string environment variable:
var db = builder.AddPostgres("postgres")
                .AddDatabase("catalogdb");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db);
This automatically injects:
ConnectionStrings__catalogdb=Host=localhost;Port=5432;Database=catalogdb;Username=postgres;Password=...
Your .NET project can then access it using configuration:
var connectionString = builder.Configuration.GetConnectionString("catalogdb");

Custom Connection String Names

Override the connection string name:
var db = builder.AddPostgres("postgres")
                .AddDatabase("catalogdb");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db, connectionName: "CatalogDatabase");
Now the environment variable becomes:
ConnectionStrings__CatalogDatabase=...

Optional References

Make a reference optional if the dependency might not be available:
var cache = builder.AddRedis("cache");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(cache, optional: true);
The application will start even if cache is unavailable, but the connection string will be empty.

Service Discovery References

For resources with HTTP endpoints (projects, containers with exposed ports), WithReference configures service discovery:
var backend = builder.AddProject<Projects.Backend>("backend")
                     .WithHttpEndpoint(port: 8080, name: "http");

var frontend = builder.AddProject<Projects.Frontend>("frontend")
                      .WithReference(backend);
This injects two types of environment variables:

1. Service Discovery Format

services__backend__http__0=http://localhost:8080
Used by .NET’s service discovery to resolve backend as a service name:
var client = httpClientFactory.CreateClient();
var response = await client.GetAsync("http://backend/api/catalog");

2. Direct Endpoint Format

BACKEND_HTTP=http://localhost:8080
Used for direct access:
var backendUrl = builder.Configuration["BACKEND_HTTP"];

Custom Service Names

Control the service name used for discovery:
var backend = builder.AddProject<Projects.Backend>("backend-api");

var frontend = builder.AddProject<Projects.Frontend>("frontend")
                      .WithReference(backend, name: "backend");
Now the service is discoverable as backend instead of backend-api:
await client.GetAsync("http://backend/api/catalog"); // Uses 'backend'

Reference by Endpoint

Reference a specific endpoint when a resource has multiple endpoints:
var backend = builder.AddProject<Projects.Backend>("backend")
                     .WithHttpEndpoint(port: 8080, name: "http")
                     .WithHttpEndpoint(port: 8443, name: "https");

var frontend = builder.AddProject<Projects.Frontend>("frontend")
                      .WithReference(backend.GetEndpoint("https"));
Only the https endpoint is injected into frontend:
services__backend__https__0=https://localhost:8443
BACKEND_HTTPS=https://localhost:8443

Reference by URI

Reference external services by URI:
var api = builder.AddProject<Projects.Api>("api")
                 .WithReference("payment-gateway", new Uri("https://api.payment.com/"));
This injects:
services__payment-gateway__default__0=https://api.payment.com/
PAYMENT_GATEWAY=https://api.payment.com/
The URI must be absolute and end with a trailing slash.

Controlling What Gets Injected

By default, WithReference injects connection strings, endpoints, and service discovery information. You can customize this with WithReferenceEnvironment:
var cache = builder.AddRedis("cache");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReferenceEnvironment(ReferenceEnvironmentInjectionFlags.ServiceDiscovery)
                 .WithReference(cache);
Available flags:
  • ConnectionString - Inject connection strings
  • Endpoints - Inject direct endpoint variables (e.g., CACHE_TCP)
  • ServiceDiscovery - Inject service discovery format (e.g., services__cache__tcp__0)
  • ConnectionProperties - Inject individual connection properties
  • All - Inject everything (default)
Combine flags:
var flags = ReferenceEnvironmentInjectionFlags.ServiceDiscovery | 
            ReferenceEnvironmentInjectionFlags.ConnectionString;

var api = builder.AddProject<Projects.Api>("api")
                 .WithReferenceEnvironment(flags)
                 .WithReference(cache);

Multiple Dependencies

Chain multiple WithReference calls:
var cache = builder.AddRedis("cache");
var db = builder.AddPostgres("postgres").AddDatabase("catalogdb");
var queue = builder.AddRabbitMQ("messaging");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(cache)
                 .WithReference(db)
                 .WithReference(queue);
All dependencies will start before api, and all connection information is injected.

Wait Behavior

Control what happens when a dependency fails to start:
By default, resources wait for dependencies to become healthy:
var db = builder.AddPostgres("postgres");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db);
The api waits for postgres to be healthy before starting.

Dependency Chains

You can create chains of dependencies:
// Database depends on migration tool
var migrator = builder.AddProject<Projects.Migrator>("migrator");

var db = builder.AddPostgres("postgres")
                .AddDatabase("catalogdb")
                .WaitFor(migrator);  // Wait for migrator to complete

// API depends on database
var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db);
Execution order:
  1. postgres starts
  2. migrator runs migrations
  3. catalogdb waits for migrator to complete
  4. api starts after catalogdb is ready

External Services

Reference services not managed by Aspire:
var externalApi = builder.AddExternalService("payment-api", 
    new Uri("https://api.payment.com/"));

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(externalApi);
Or use a parameter for dynamic configuration:
var apiUrl = builder.AddParameter("payment-api-url");
var externalApi = builder.AddExternalService("payment-api", apiUrl);

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(externalApi);

Debugging Dependencies

View the resource graph in the Aspire Dashboard:
  1. Start your application
  2. Open the dashboard
  3. Click on a resource
  4. View the Dependencies tab
The dashboard shows:
  • Which resources this resource depends on
  • Which resources depend on this resource
  • The startup order

Common Patterns

var db = builder.AddPostgres("postgres")
                .AddDatabase("appdb");

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db);
var api = builder.AddProject<Projects.Api>("api");

var web = builder.AddProject<Projects.Web>("web")
                 .WithReference(api);
var cache = builder.AddRedis("cache");
var queue = builder.AddRabbitMQ("messaging");

var catalog = builder.AddProject<Projects.CatalogService>("catalog")
                     .WithReference(cache)
                     .WithReference(queue);

var orders = builder.AddProject<Projects.OrderService>("orders")
                    .WithReference(cache)
                    .WithReference(queue);

var frontend = builder.AddProject<Projects.Frontend>("frontend")
                      .WithReference(catalog)
                      .WithReference(orders);
var db = builder.AddPostgres("postgres")
                .AddDatabase("appdb");

var migrator = builder.AddProject<Projects.Migrator>("migrator")
                      .WithReference(db);

db.WaitFor(migrator);

var api = builder.AddProject<Projects.Api>("api")
                 .WithReference(db);

Next Steps

Configuration

Learn about configuration management

Environment Variables

Control environment variable injection

Build docs developers (and LLMs) love