A URL such as http://localhost:5237 is a local runtime detail, not an application contract. The port can change between machines, launch profiles, test runs, and deployment environments.
Model the dependency in the Aspire AppHost and pass it to the consuming project with WithReference():
var catalog = builder.AddProject<Projects.CatalogApi>("catalog");
builder.AddProject<Projects.WebFrontend>("frontend")
.WithReference(catalog);
The consuming application also needs the service discovery handler. The standard Aspire template configures it through AddServiceDefaults():
builder.AddServiceDefaults();
builder.Services.AddHttpClient<CatalogClient>(client =>
{
client.BaseAddress = new Uri("https+http://catalog");
});
AddServiceDefaults() is an extension from the generated ServiceDefaults project. It registers service discovery and configures HttpClient to use it. If you replace the template defaults, register service discovery and add its handler to the client yourself.
Aspire injects the endpoint information for referenced services. The resolver translates the logical catalog name into the address assigned to that resource at runtime. The special https+http:// scheme asks service discovery to prefer HTTPS and fall back to HTTP when both schemes are configured. Application code no longer needs to know which local port the AppHost selected.
The same logical name can be backed by deployment-specific configuration, so the application does not need local-only URLs baked into code.
If a resource exposes multiple endpoints, address the required endpoint explicitly. For example, https+http://_admin.catalog selects the endpoint named admin on the catalog resource.
Keep references explicit. If the frontend needs the catalog API, add the reference. If it does not, do not give it discovery information by default. The application graph then documents which services are intended to discover each other. WithReference() provides discovery information, not authorization.
WithReference() is not a readiness guarantee. Add health checks and use WaitFor(catalog) when the frontend must wait until the catalog is running and healthy. Use WaitForStart(catalog) when waiting for the running state is enough and the client can handle initial connection failures. Service discovery also does not replace authentication, authorization, timeouts, or HTTP resilience.
Hardcoded addresses are still reasonable for a true external endpoint supplied through configuration. For services owned by the same Aspire application, prefer a logical reference and let the environment provide the actual address.