diff --git a/src/frontend/config/redirects.mjs b/src/frontend/config/redirects.mjs
index e8296decf..0e55e57ba 100644
--- a/src/frontend/config/redirects.mjs
+++ b/src/frontend/config/redirects.mjs
@@ -4,6 +4,7 @@ export const redirects = {
// '/original/path/': '/new/path'
'/get-started/welcome/': '/docs/',
'/integrations/postgres/': '/integrations/databases/postgres/postgres-get-started/',
+ '/integrations/databases/postgres/postgres-client/': '/integrations/databases/postgres/postgres-connect/',
'/integrations/databases/milvus/': '/integrations/databases/milvus/milvus-get-started/',
'/integrations/databases/qdrant/': '/integrations/databases/qdrant/qdrant-get-started/',
'/integrations/oracle/': '/integrations/databases/efcore/oracle/oracle-get-started/',
diff --git a/src/frontend/config/sidebar/integrations.topics.ts b/src/frontend/config/sidebar/integrations.topics.ts
index 915014297..9e55695f3 100644
--- a/src/frontend/config/sidebar/integrations.topics.ts
+++ b/src/frontend/config/sidebar/integrations.topics.ts
@@ -1189,10 +1189,6 @@ export const integrationTopics: StarlightSidebarTopicsUserConfig = {
label: 'Connect to PostgreSQL',
slug: 'integrations/databases/postgres/postgres-connect',
},
- {
- label: 'Client integration (.NET)',
- slug: 'integrations/databases/postgres/postgres-client',
- },
{
label: 'Community extensions',
slug: 'integrations/databases/postgres/postgresql-extensions',
diff --git a/src/frontend/src/components/starlight/Head.astro b/src/frontend/src/components/starlight/Head.astro
index a849b8900..f2da6a7be 100644
--- a/src/frontend/src/components/starlight/Head.astro
+++ b/src/frontend/src/components/starlight/Head.astro
@@ -42,28 +42,112 @@ function computeSourceUrl() {
diff --git a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-client.mdx b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-client.mdx
deleted file mode 100644
index d4dfb243f..000000000
--- a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-client.mdx
+++ /dev/null
@@ -1,187 +0,0 @@
----
-title: PostgreSQL Client integration reference
-description: Learn how to use the Aspire PostgreSQL Client integration to query PostgreSQL databases from your Aspire projects.
----
-
-import { Image } from 'astro:assets';
-import InstallPackage from '@components/InstallPackage.astro';
-import InstallDotNetPackage from '@components/InstallDotNetPackage.astro';
-import {
- Aside,
- Code,
- Steps,
- Tabs,
- TabItem,
-} from '@astrojs/starlight/components';
-import postgresIcon from '@assets/icons/postgresql-icon.png';
-
-
-
-To get started with the Aspire PostgreSQL integrations, follow the [Get started with PostgreSQL integrations](../postgres-get-started/) guide.
-
-This article covers the Aspire PostgreSQL **Client integration** for .NET applications. It uses the [📦 Aspire.Npgsql](https://www.nuget.org/packages/Aspire.Npgsql) NuGet package to connect to and interact with PostgreSQL databases from your .NET consuming projects.
-
-
-
-## Installation
-
-To get started with the Aspire PostgreSQL client integration, install the [📦 Aspire.Npgsql](https://www.nuget.org/packages/Aspire.Npgsql) NuGet package in the client-consuming project, that is, the project for the application that uses the PostgreSQL client. The PostgreSQL client integration registers an [NpgsqlDataSource](https://www.npgsql.org/doc/api/Npgsql.NpgsqlDataSource.html) instance that you can use to interact with PostgreSQL.
-
-
-
-## Add Npgsql client
-
-In the `Program.cs` file of your client-consuming project, call the `AddNpgsqlDataSource` extension method on any `IHostApplicationBuilder` to register an `NpgsqlDataSource` for use via the dependency injection container. The method takes a connection name parameter.
-
-```csharp title="C# — Program.cs"
-builder.AddNpgsqlDataSource(connectionName: "postgresdb");
-```
-
-
-
-After adding `NpgsqlDataSource` to the builder, you can get the `NpgsqlDataSource` instance using dependency injection. For example, to retrieve your data source object from an example service define it as a constructor parameter and ensure the `ExampleService` class is registered with the dependency injection container:
-
-```csharp title="C# — ExampleService.cs"
-public class ExampleService(NpgsqlDataSource dataSource)
-{
- // Use dataSource...
-}
-```
-
-## Add keyed Npgsql client
-
-There might be situations where you want to register multiple `NpgsqlDataSource` instances with different connection names. To register keyed Npgsql clients, call the `AddKeyedNpgsqlDataSource` method:
-
-```csharp title="C# — Program.cs"
-builder.AddKeyedNpgsqlDataSource(name: "chat");
-builder.AddKeyedNpgsqlDataSource(name: "queue");
-```
-
-Then you can retrieve the `NpgsqlDataSource` instances using dependency injection. For example, to retrieve the connection from an example service:
-
-```csharp title="C# — ExampleService.cs"
-public class ExampleService(
- [FromKeyedServices("chat")] NpgsqlDataSource chatDataSource,
- [FromKeyedServices("queue")] NpgsqlDataSource queueDataSource)
-{
- // Use data sources...
-}
-```
-
-## Properties of the PostgreSQL resources
-
-For a full reference of PostgreSQL connection properties and environment variables, see [Connect to PostgreSQL](../postgres-connect/).
-
-## Configuration
-
-The Aspire PostgreSQL integration provides multiple configuration approaches and options to meet the requirements and conventions of your project.
-
-### Use a connection string
-
-When using a connection string from the `ConnectionStrings` configuration section, you can provide the name of the connection string when calling the `AddNpgsqlDataSource` method:
-
-```csharp title="C# — Program.cs"
-builder.AddNpgsqlDataSource("postgresdb");
-```
-
-Then the connection string will be retrieved from the `ConnectionStrings` configuration section:
-
-```json title="JSON — appsettings.json"
-{
- "ConnectionStrings": {
- "postgresdb": "Host=myserver;Database=postgresdb"
- }
-}
-```
-
-For more information, see the [ConnectionString](https://www.npgsql.org/doc/connection-string-parameters.html).
-
-### Use configuration providers
-
-The Aspire PostgreSQL integration supports `Microsoft.Extensions.Configuration`. It loads the `NpgsqlSettings` from `appsettings.json` or other configuration files by using the `Aspire:Npgsql` key. Example `appsettings.json` that configures some of the options:
-
-The following example shows an `appsettings.json` file that configures some of the available options:
-
-```json title="JSON — appsettings.json"
-{
- "Aspire": {
- "Npgsql": {
- "ConnectionString": "Host=myserver;Database=postgresdb",
- "DisableHealthChecks": false,
- "DisableTracing": true,
- "DisableMetrics": false
- }
- }
-}
-```
-
-For the complete PostgreSQL client integration JSON schema, see [Aspire.Npgsql/ConfigurationSchema.json](https://github.com/microsoft/aspire/blob/v9.1.0/src/Components/Aspire.Npgsql/ConfigurationSchema.json).
-
-### Use inline delegates
-
-You can also pass the `Action configureSettings` delegate to set up some or all the options inline, for example to disable health checks:
-
-```csharp title="C# — Program.cs"
-builder.AddNpgsqlDataSource(
- "postgresdb",
- static settings => settings.DisableHealthChecks = true);
-```
-
-## Client integration health checks
-
-By default, Aspire _client integrations_ have health checks enabled for all services. Similarly, many Aspire _hosting integrations_ also enable health check endpoints. For more information, see:
-
-- Adds the [`NpgSqlHealthCheck`](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/blob/master/src/HealthChecks.NpgSql/NpgSqlHealthCheck.cs), which verifies that commands can be successfully executed against the underlying Postgres database.
-- Integrates with the `/health` HTTP endpoint, which specifies all registered health checks must pass for app to be considered ready to accept traffic
-
-## Observability and telemetry
-
-Aspire integrations automatically set up Logging, Tracing, and Metrics configurations, which are sometimes known as _the pillars of observability_. Depending on the backing service, some integrations may only support some of these features. For example, some integrations support logging and tracing, but not metrics. Telemetry features can also be disabled using the techniques presented in the [Configuration](#configuration) section.
-
-### Logging
-
-The Aspire PostgreSQL integration uses the following log categories:
-
-- `Npgsql.Connection`
-- `Npgsql.Command`
-- `Npgsql.Transaction`
-- `Npgsql.Copy`
-- `Npgsql.Replication`
-- `Npgsql.Exception`
-
-### Tracing
-
-The Aspire PostgreSQL integration will emit the following tracing activities using OpenTelemetry:
-
-- `Npgsql`
-
-### Metrics
-
-The Aspire PostgreSQL integration will emit the following metrics using OpenTelemetry:
-
-- Npgsql:
- - `ec_Npgsql_bytes_written_per_second`
- - `ec_Npgsql_bytes_read_per_second`
- - `ec_Npgsql_commands_per_second`
- - `ec_Npgsql_total_commands`
- - `ec_Npgsql_current_commands`
- - `ec_Npgsql_failed_commands`
- - `ec_Npgsql_prepared_commands_ratio`
- - `ec_Npgsql_connection_pools`
- - `ec_Npgsql_multiplexing_average_commands_per_batch`
- - `ec_Npgsql_multiplexing_average_write_time_per_batch`
diff --git a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-connect.mdx b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-connect.mdx
index 6fa99281b..6bc08fb7e 100644
--- a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-connect.mdx
+++ b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-connect.mdx
@@ -1,10 +1,11 @@
---
title: Connect to PostgreSQL
-description: Learn how to connect to PostgreSQL from any language when using Aspire to host your PostgreSQL resources.
+description: Learn how to connect to PostgreSQL from C#, Go, Python, and TypeScript consuming apps in an Aspire solution.
---
import { Image } from 'astro:assets';
import { Aside, Tabs, TabItem } from '@astrojs/starlight/components';
+import InstallDotNetPackage from '@components/InstallDotNetPackage.astro';
import postgresIcon from '@assets/icons/postgresql-icon.png';
-When you reference a PostgreSQL resource from the AppHost with `WithReference` (C#) or `withReference` (TypeScript), Aspire automatically injects connection information into the consuming application as environment variables. This page shows how to read those variables and connect to PostgreSQL from any language.
+This page describes how consuming apps connect to a PostgreSQL resource that's already modeled in your AppHost. For the AppHost API surface — adding a PostgreSQL server, databases, pgAdmin, pgWeb, volumes, and more — see [PostgreSQL Hosting integration](../postgres-host/).
-
+When you reference a PostgreSQL resource from your AppHost, Aspire injects the connection information into the consuming app as environment variables. Your app can either read those environment variables directly — the pattern works the same from any language — or, in C#, use the Aspire PostgreSQL client integration for automatic dependency injection, health checks, and telemetry.
## Connection properties
@@ -63,83 +62,170 @@ Uri: postgresql://postgres:p%40ssw0rd1@localhost:5432/catalog
JdbcConnectionString: jdbc:postgresql://localhost:5432/catalog
```
-## Connect from your application
+## Connect from your app
-The following examples show how to connect to PostgreSQL from different languages. Each example assumes you have a PostgreSQL database resource named `postgresdb` referenced from your AppHost.
+Pick the language your consuming app is written in. Each example assumes your AppHost adds a PostgreSQL database resource named `postgresdb` and references it from the consuming app.
-
-
+
+
-Install the PostgreSQL client library:
+For C# apps, the recommended approach is the Aspire PostgreSQL client integration. It registers an [`NpgsqlDataSource`](https://www.npgsql.org/doc/api/Npgsql.NpgsqlDataSource.html) through dependency injection and adds health checks and telemetry automatically. If you'd rather read environment variables directly, see the [Read environment variables](#read-environment-variables-in-c) section at the end of this tab.
-```bash title="Terminal"
-npm install pg
+#### Install the client integration
+
+Install the [📦 Aspire.Npgsql](https://www.nuget.org/packages/Aspire.Npgsql) NuGet package in the client-consuming project:
+
+
+
+#### Add the Npgsql data source
+
+In _Program.cs_, call `AddNpgsqlDataSource` on your `IHostApplicationBuilder` to register an `NpgsqlDataSource`. The `connectionName` must match the PostgreSQL database resource name from the AppHost:
+
+```csharp title="C# — Program.cs"
+builder.AddNpgsqlDataSource(connectionName: "postgresdb");
```
-Read the injected environment variables and connect:
+Resolve the data source through dependency injection:
-```javascript title="JavaScript — index.js"
-import pg from 'pg';
+```csharp title="C# — ExampleService.cs"
+public class ExampleService(NpgsqlDataSource dataSource)
+{
+ // Use dataSource...
+}
+```
-// Read Aspire-injected connection properties
-const client = new pg.Client({
- user: process.env.POSTGRESDB_USERNAME,
- host: process.env.POSTGRESDB_HOST,
- database: process.env.POSTGRESDB_DATABASENAME,
- password: process.env.POSTGRESDB_PASSWORD,
- port: process.env.POSTGRESDB_PORT,
-});
+#### Add keyed Npgsql clients
-await client.connect();
+To register multiple `NpgsqlDataSource` instances with different connection names, use `AddKeyedNpgsqlDataSource`:
+
+```csharp title="C# — Program.cs"
+builder.AddKeyedNpgsqlDataSource(name: "chat");
+builder.AddKeyedNpgsqlDataSource(name: "queue");
```
-Or use the connection URI directly:
+Then resolve each instance by key:
-```javascript title="JavaScript — Connect with URI"
-const client = new pg.Client({
- connectionString: process.env.POSTGRESDB_URI,
-});
+```csharp title="C# — ExampleService.cs"
+public class ExampleService(
+ [FromKeyedServices("chat")] NpgsqlDataSource chatDataSource,
+ [FromKeyedServices("queue")] NpgsqlDataSource queueDataSource)
+{
+ // Use data sources...
+}
+```
-await client.connect();
+#### Configuration
+
+The Aspire PostgreSQL client integration offers multiple ways to provide configuration.
+
+**Connection strings.** When using a connection string from the `ConnectionStrings` configuration section, pass the connection name to `AddNpgsqlDataSource`:
+
+```csharp title="C# — Program.cs"
+builder.AddNpgsqlDataSource("postgresdb");
```
-
-
+The connection string is resolved from the `ConnectionStrings` section:
-Install a PostgreSQL driver. This example uses `psycopg`:
+```json title="JSON — appsettings.json"
+{
+ "ConnectionStrings": {
+ "postgresdb": "Host=myserver;Database=postgresdb"
+ }
+}
+```
-```bash title="Terminal"
-pip install psycopg[binary]
+For more information, see [Npgsql connection string parameters](https://www.npgsql.org/doc/connection-string-parameters.html).
+
+**Configuration providers.** The client integration supports `Microsoft.Extensions.Configuration`. It loads `NpgsqlSettings` from _appsettings.json_ (or any other configuration source) by using the `Aspire:Npgsql` key:
+
+```json title="JSON — appsettings.json"
+{
+ "Aspire": {
+ "Npgsql": {
+ "ConnectionString": "Host=myserver;Database=postgresdb",
+ "DisableHealthChecks": false,
+ "DisableTracing": true,
+ "DisableMetrics": false
+ }
+ }
+}
```
-Read the injected environment variables and connect:
+For the complete PostgreSQL client integration JSON schema, see [Aspire.Npgsql/ConfigurationSchema.json](https://github.com/microsoft/aspire/blob/v9.1.0/src/Components/Aspire.Npgsql/ConfigurationSchema.json).
-```python title="Python — app.py"
-import os
-import psycopg
+**Inline delegates.** Pass an `Action` to configure settings inline, for example to disable health checks:
-# Read the Aspire-injected connection URI
-postgres_uri = os.getenv("POSTGRESDB_URI")
+```csharp title="C# — Program.cs"
+builder.AddNpgsqlDataSource(
+ "postgresdb",
+ static settings => settings.DisableHealthChecks = true);
+```
-async with await psycopg.AsyncConnection.connect(
- postgres_uri, autocommit=True
-) as conn:
- # Use conn to query the database...
- pass
+#### Client integration health checks
+
+Aspire client integrations enable health checks by default. The PostgreSQL client integration adds:
+
+- The [`NpgSqlHealthCheck`](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/blob/master/src/HealthChecks.NpgSql/NpgSqlHealthCheck.cs), which verifies that commands can be successfully executed against the underlying PostgreSQL database.
+- Integration with the `/health` HTTP endpoint, where all registered health checks must pass before the app is considered ready to accept traffic.
+
+#### Observability and telemetry
+
+The Aspire PostgreSQL client integration automatically configures logging, tracing, and metrics through OpenTelemetry.
+
+**Logging** categories:
+
+- `Npgsql.Connection`
+- `Npgsql.Command`
+- `Npgsql.Transaction`
+- `Npgsql.Copy`
+- `Npgsql.Replication`
+- `Npgsql.Exception`
+
+**Tracing** activities:
+
+- `Npgsql`
+
+**Metrics**:
+
+- `ec_Npgsql_bytes_written_per_second`
+- `ec_Npgsql_bytes_read_per_second`
+- `ec_Npgsql_commands_per_second`
+- `ec_Npgsql_total_commands`
+- `ec_Npgsql_current_commands`
+- `ec_Npgsql_failed_commands`
+- `ec_Npgsql_prepared_commands_ratio`
+- `ec_Npgsql_connection_pools`
+- `ec_Npgsql_multiplexing_average_commands_per_batch`
+- `ec_Npgsql_multiplexing_average_write_time_per_batch`
+
+Any of these telemetry features can be disabled through the configuration options above.
+
+#### Read environment variables in C\#
+
+If you prefer to read environment variables directly without the client integration:
+
+```csharp title="C# — Program.cs"
+var connectionString = Environment.GetEnvironmentVariable(
+ "POSTGRESDB_URI");
+
+await using var dataSource = NpgsqlDataSource.Create(connectionString!);
+await using var conn = await dataSource.OpenConnectionAsync();
```
-Use the `pgx` driver, the most actively maintained and feature-rich PostgreSQL driver for Go:
+Use the `pgx` driver, the most actively maintained PostgreSQL driver for Go:
```bash title="Terminal"
go get github.com/jackc/pgx/v5
```
-> **Note:** [`lib/pq`](https://github.com/lib/pq) is another popular Go PostgreSQL driver, but `pgx` is recommended for new projects because it offers better performance, native PostgreSQL protocol support, and active maintenance.
+
-Read the injected environment variables and connect:
+Read the injected environment variable and connect:
```go title="Go — main.go"
package main
@@ -163,65 +249,70 @@ func main() {
```
-
+
-For .NET applications, the recommended approach is to use the [Client integration (.NET)](../postgres-client/), which provides automatic dependency injection, health checks, and telemetry.
+Install a PostgreSQL driver. This example uses `psycopg`:
-If you prefer to connect using environment variables directly:
+```bash title="Terminal"
+pip install psycopg[binary]
+```
-```csharp title="C# — Program.cs"
-var connectionString = Environment.GetEnvironmentVariable(
- "POSTGRESDB_URI");
+Read the injected environment variable and connect:
-await using var dataSource = NpgsqlDataSource.Create(connectionString!);
-await using var conn = await dataSource.OpenConnectionAsync();
+```python title="Python — app.py"
+import os
+import psycopg
+
+# Read the Aspire-injected connection URI
+postgres_uri = os.getenv("POSTGRESDB_URI")
+
+async with await psycopg.AsyncConnection.connect(
+ postgres_uri, autocommit=True
+) as conn:
+ # Use conn to query the database...
+ pass
```
-
+
-## Passing custom environment variables from the AppHost
+Install the PostgreSQL client library:
-If your application expects specific environment variable names, you can pass individual connection properties from the AppHost:
+```bash title="Terminal"
+npm install pg
+npm install --save-dev @types/pg
+```
-
-
-```csharp title="C# — AppHost.cs"
-var builder = DistributedApplication.CreateBuilder(args);
+Read the injected environment variables and connect:
-var postgres = builder.AddPostgres("postgres");
-var database = postgres.AddDatabase("myDatabase");
+```typescript title="TypeScript — index.ts"
+import { Client } from 'pg';
-var app = builder.AddExecutable("my-app", "node", "app.js", ".")
- .WithReference(database)
- .WithEnvironment(context =>
- {
- context.EnvironmentVariables["POSTGRES_HOST"] = postgres.Resource.PrimaryEndpoint.Property(EndpointProperty.Host);
- context.EnvironmentVariables["POSTGRES_PORT"] = postgres.Resource.PrimaryEndpoint.Property(EndpointProperty.Port);
- context.EnvironmentVariables["POSTGRES_USER"] = postgres.Resource.UserNameParameter;
- context.EnvironmentVariables["POSTGRES_PASSWORD"] = postgres.Resource.PasswordParameter;
- context.EnvironmentVariables["POSTGRES_DATABASE"] = database.Resource.DatabaseName;
- });
+// Read Aspire-injected connection properties
+const client = new Client({
+ user: process.env.POSTGRESDB_USERNAME,
+ host: process.env.POSTGRESDB_HOST,
+ database: process.env.POSTGRESDB_DATABASENAME,
+ password: process.env.POSTGRESDB_PASSWORD,
+ port: Number(process.env.POSTGRESDB_PORT),
+});
-builder.Build().Run();
+await client.connect();
```
-
-
-```typescript title="TypeScript — apphost.ts" twoslash
-import { createBuilder } from './.modules/aspire.js';
-
-const builder = await createBuilder();
-const postgres = await builder.addPostgres("postgres");
-const database = await postgres.addDatabase("myDatabase");
+Or use the connection URI directly:
-await builder.addNodeApp("my-app", "./app", "index.js")
- .withReference(database)
- .withEnvironment("POSTGRES_USER", postgres.userNameParameter)
- .withEnvironment("POSTGRES_PASSWORD", postgres.passwordParameter)
- .withEnvironment("POSTGRES_DATABASE", "myDatabase");
+```typescript title="TypeScript — Connect with URI"
+const client = new Client({
+ connectionString: process.env.POSTGRESDB_URI,
+});
-await builder.build().run();
+await client.connect();
```
+
+
+
diff --git a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-get-started.mdx b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-get-started.mdx
index f6c449e26..7bf71c951 100644
--- a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-get-started.mdx
+++ b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-get-started.mdx
@@ -1,13 +1,10 @@
---
title: Get started with the PostgreSQL integrations
-description: Learn how to set up the Aspire PostgreSQL Hosting and Client integrations simply.
+description: Understand how the Aspire PostgreSQL integrations fit together — model a database resource in your AppHost, then connect to it from any consuming app.
---
import { Image } from 'astro:assets';
-import InstallDotNetPackage from '@components/InstallDotNetPackage.astro';
-import { Aside, CardGrid, LinkCard, Tabs, TabItem } from '@astrojs/starlight/components';
-import PivotSelector from '@components/PivotSelector.astro';
-import Pivot from '@components/Pivot.astro';
+import { LinkButton, Steps } from '@astrojs/starlight/components';
import postgresIcon from '@assets/icons/postgresql-icon.png';
-[PostgreSQL](https://www.postgresql.org/) is a powerful, open source object-relational database system with many years of active development that has earned it a strong reputation for reliability, feature robustness, and performance. The Aspire PostgreSQL integration provides a way to connect to existing PostgreSQL databases, or create new instances from the [`docker.io/library/postgres` container image](https://hub.docker.com/_/postgres).
+[PostgreSQL](https://www.postgresql.org/) is a mature, open-source object-relational database with a strong reputation for reliability, feature richness, and performance. The Aspire PostgreSQL integration lets you model a PostgreSQL server and its databases as first-class resources in your AppHost, then hand the connection information to any consuming app — regardless of language.
-Use this guide to add PostgreSQL to an Aspire solution. Choose the AppHost language for your solution first, then follow the matching quickstart. For the full PostgreSQL hosting API surface in either C# or TypeScript, see [PostgreSQL Hosting integration](../postgres-host/).
+## Why use PostgreSQL with Aspire
-
+Adding PostgreSQL through Aspire — rather than wiring up containers and connection strings by hand — gives you:
-## Set up the PostgreSQL Hosting integration
+- **Zero-config local development.** Aspire runs PostgreSQL from the [`docker.io/library/postgres`](https://hub.docker.com/_/postgres) container image with credentials generated automatically for you.
+- **Consistent connection info across languages.** Once you reference the database from a consuming app, Aspire injects connection properties as environment variables in a predictable format that works from C#, TypeScript, Python, Go, or any other language.
+- **Built-in health checks.** The hosting integration automatically registers a health check so the dashboard and your orchestrator can tell when the server is ready.
+- **Dashboard observability.** The database resource shows up in the Aspire dashboard with logs, status, and telemetry alongside your other services.
+- **A first-class C# client integration.** C# apps can use the `Aspire.Npgsql` package for dependency injection, health checks, and OpenTelemetry, all wired up from the same resource name.
+- **An upgrade path to managed Azure.** The same AppHost model extends to [Azure Database for PostgreSQL](/integrations/cloud/azure/azure-postgresql/azure-postgresql-get-started/) when you're ready to deploy.
-Choose your AppHost language, then add PostgreSQL to the AppHost. For the full C# and TypeScript hosting API surface, see [PostgreSQL Hosting integration](../postgres-host/).
+## How the pieces fit together
-
-
-
-
-## TypeScript AppHost quickstart
-
-Use this path when your Aspire solution is modeled in `apphost.ts`.
-
-### Add the PostgreSQL Hosting integration with the Aspire CLI
-
-```bash title="Terminal"
-aspire add postgres
-```
-
-This updates your `aspire.config.json` with the PostgreSQL hosting integration package:
-
-```json title="aspire.config.json" ins={3}
-{
- "packages": {
- "Aspire.Hosting.PostgreSQL": "*"
- }
-}
-```
-
-### Model PostgreSQL resources in `apphost.ts`
-
-```typescript title="apphost.ts" twoslash
-import { createBuilder } from './.modules/aspire.js';
-
-const builder = await createBuilder();
-
-const postgres = await builder.addPostgres("postgres");
-const postgresdb = await postgres.addDatabase("postgresdb");
-
-await builder.addNodeApp("api", "./api", "index.js")
- .withExternalHttpEndpoints()
- .waitFor(postgresdb)
- .withReference(postgresdb);
-```
-
-
-
-### Connect from your consuming app
-
-The examples below assume your `apphost.ts` file references `postgresdb` from the consuming app with `withReference(postgresdb)`.
-
-
-
-
-Install the PostgreSQL client library in your JavaScript or TypeScript consuming project:
-
-```bash title="Terminal"
-npm install pg
-```
-
-Import `pg` in code files that interact with the database:
-
-```javascript title="JavaScript - Import pg"
-import pg from 'pg';
-```
-
-Use the `process.env` object to read the injected PostgreSQL properties:
-
-```javascript title="JavaScript - Obtain configuration properties"
-const pguser = process.env.POSTGRESDB_USERNAME;
-const pgpassword = process.env.POSTGRESDB_PASSWORD;
-const pghost = process.env.POSTGRESDB_HOST;
-const pgport = process.env.POSTGRESDB_PORT;
-const pgdatabase = process.env.POSTGRESDB_DATABASENAME;
-```
-
-Connect to PostgreSQL with `pg`:
-
-```javascript title="JavaScript - Connect to PostgreSQL"
-const client = new pg.Client({
- user: pguser,
- host: pghost,
- database: pgdatabase,
- password: pgpassword,
- port: pgport,
-});
-
-await client.connect();
-```
-
-
-
-
-Install a PostgreSQL driver in your Python consuming project. This example uses `psycopg`:
-
-```bash title="Terminal"
-pip install psycopg[binary]
-```
-
-Import `os` and `psycopg` in code files that interact with the database:
-
-```python title="Python - Import psycopg"
-import os
-import psycopg
-```
-
-Use `os.getenv()` to read the injected PostgreSQL properties:
-
-```python title="Python - Obtain configuration properties"
-postgres_uri = os.getenv("POSTGRESDB_URI")
-postgres_name = os.getenv("POSTGRESDB_DATABASENAME")
-```
-
-Connect to PostgreSQL with `psycopg`:
-
-```python title="Python - Connect to PostgreSQL"
-async with await psycopg.AsyncConnection.connect(postgres_uri, autocommit=True) as conn:
-```
-
-
-
-
-Install the Aspire PostgreSQL client integration in each C# consuming project:
-
-
-
-In `Program.cs`, call the `AddNpgsqlDataSource` extension method on an `IHostApplicationBuilder` to register an `NpgsqlDataSource` for use via dependency injection:
-
-```csharp title="C# - Program.cs"
-builder.AddNpgsqlDataSource(connectionName: "postgresdb");
-```
-
-
-
-Get the `NpgsqlDataSource` instance by using dependency injection:
-
-```csharp title="C# - ExampleService.cs"
-public class ExampleService(NpgsqlDataSource dataSource)
-{
- // Use dataSource to query the database...
-}
-```
-
-
-
-
-
-
-
-
-## C# AppHost quickstart
-
-Use this path when your Aspire solution is modeled in `AppHost.cs`.
-
-### Add the PostgreSQL Hosting integration to the AppHost
-
-```bash title="Terminal"
-aspire add postgres
-```
+The PostgreSQL integration has two sides: a **hosting integration** that you use in your AppHost to model the database resource, and a **connection story** for consuming apps that reference it.
-Or, add the package manually in either `AppHost.cs` or `AppHost.csproj`:
+```mermaid
+architecture-beta
-```csharp title="C# - AppHost.cs"
-#:package Aspire.Hosting.PostgreSQL@*
-```
-
-```xml title="XML - AppHost.csproj"
-
-```
-
-### Model PostgreSQL resources in `AppHost.cs`
+ group apphost[AppHost]
-```csharp title="AppHost.cs"
-var builder = DistributedApplication.CreateBuilder(args);
+ service postgres(logos:postgresql)[PostgreSQL server] in apphost
+ service db(database)[postgresdb] in apphost
-var postgres = builder.AddPostgres("postgres");
-var postgresdb = postgres.AddDatabase("postgresdb");
+ service app(server)[Consuming app]
-var api = builder.AddProject("apiservice")
- .WaitFor(postgresdb)
- .WithReference(postgresdb);
+ postgres:R --> L:db
+ db:R --> L:app
```
-
-
-### Connect from your consuming app
-
-The examples below assume your `AppHost.cs` file references `postgresdb` from the consuming app with `WithReference(postgresdb)`.
-
-
-
+Getting there is a two-step process: model the PostgreSQL resources in your AppHost, then connect to the database from each app that needs it.
-Install the Aspire PostgreSQL client integration in each C# consuming project:
+
-
+1. ### Model PostgreSQL in your AppHost
-In `Program.cs`, call the `AddNpgsqlDataSource` extension method on an `IHostApplicationBuilder` to register an `NpgsqlDataSource` for use via dependency injection:
-
-```csharp title="C# - Program.cs"
-builder.AddNpgsqlDataSource(connectionName: "postgresdb");
-```
-
-
-
-Get the `NpgsqlDataSource` instance by using dependency injection:
-
-```csharp title="C# - ExampleService.cs"
-public class ExampleService(NpgsqlDataSource dataSource)
-{
- // Use dataSource to query the database...
-}
-```
+ Add the PostgreSQL hosting integration to your AppHost, then declare a PostgreSQL server, one or more databases, and reference them from the apps that need to talk to the database. The [PostgreSQL Hosting integration](/integrations/databases/postgres/postgres-host/) reference walks through every capability — adding databases, pgAdmin, pgWeb, data volumes, init scripts, custom parameters, and more — with side-by-side C# and TypeScript examples.
-
-
+
+ Add PostgreSQL hosting integration
+
-Install a PostgreSQL driver in your Python consuming project. This example uses `psycopg`:
-
-```bash title="Terminal"
-pip install psycopg[binary]
-```
-
-Import `os` and `psycopg` in code files that interact with the database:
-
-```python title="Python - Import psycopg"
-import os
-import psycopg
-```
-
-Use `os.getenv()` to read the injected PostgreSQL properties:
-
-```python title="Python - Obtain configuration properties"
-postgres_uri = os.getenv("POSTGRESDB_URI")
-postgres_name = os.getenv("POSTGRESDB_DATABASENAME")
-```
-
-Connect to PostgreSQL with `psycopg`:
-
-```python title="Python - Connect to PostgreSQL"
-async with await psycopg.AsyncConnection.connect(postgres_uri, autocommit=True) as conn:
-```
-
-
-
-
-Install the PostgreSQL client library in your JavaScript or TypeScript consuming project:
-
-```bash title="Terminal"
-npm install pg
-```
-
-Import `pg` in code files that interact with the database:
-
-```javascript title="JavaScript - Import pg"
-import pg from 'pg';
-```
-
-Use the `process.env` object to read the injected PostgreSQL properties:
-
-```javascript title="JavaScript - Obtain configuration properties"
-const pguser = process.env.POSTGRESDB_USERNAME;
-const pgpassword = process.env.POSTGRESDB_PASSWORD;
-const pghost = process.env.POSTGRESDB_HOST;
-const pgport = process.env.POSTGRESDB_PORT;
-const pgdatabase = process.env.POSTGRESDB_DATABASENAME;
-```
-
-Connect to PostgreSQL with `pg`:
-
-```javascript title="JavaScript - Connect to PostgreSQL"
-const client = new pg.Client({
- user: pguser,
- host: pghost,
- database: pgdatabase,
- password: pgpassword,
- port: pgport,
-});
-
-await client.connect();
-```
+2. ### Connect from your consuming app
-
-
+ When you reference a PostgreSQL database from a consuming app, Aspire injects its connection information as environment variables. See [Connect to PostgreSQL](/integrations/databases/postgres/postgres-connect/) for the connection properties reference and per-language examples for C#, Go, Python, and TypeScript — including the full C# client integration.
-
+
+ Connect to PostgreSQL
+
-## Next steps
+
-Now that you have an Aspire app with PostgreSQL integrations up and running, you can use the following reference documents to learn how to configure and interact with the PostgreSQL resources:
+## See also
-
-
-
-
-
+- [PostgreSQL community extensions](/integrations/databases/postgres/postgresql-extensions/)
diff --git a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-host.mdx b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-host.mdx
index bde6211e5..a5286a85c 100644
--- a/src/frontend/src/content/docs/integrations/databases/postgres/postgres-host.mdx
+++ b/src/frontend/src/content/docs/integrations/databases/postgres/postgres-host.mdx
@@ -5,7 +5,7 @@ description: Learn how to use the Aspire PostgreSQL Hosting integration to orche
import { Image } from 'astro:assets';
import { Aside, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
-import PivotSelector from '@components/PivotSelector.astro';
+import LearnMore from '@components/LearnMore.astro';
import postgresIcon from '@assets/icons/postgresql-icon.png';
-To get started with the Aspire PostgreSQL integrations, follow the [Get started with PostgreSQL integrations](/integrations/databases/postgres/postgres-get-started/) guide. If you want to learn how to use the PostgreSQL Entity Framework Core (EF Core) client integration, see [Get started with the PostgreSQL Entity Framework Core integrations](/integrations/databases/efcore/postgres/postgresql-get-started/).
+This article is the reference for the Aspire PostgreSQL Hosting integration. It enumerates the AppHost APIs — with examples for both `AppHost.cs` and `apphost.ts` — that you use to model PostgreSQL server and database resources in your [`AppHost`](/get-started/app-host/) project.
-This article includes full details on the Aspire PostgreSQL Hosting integration, with examples for both `AppHost.cs` and `apphost.ts`, so you can model PostgreSQL server and database resources in your [`AppHost`](/get-started/app-host/) project.
-
-
-
-Use this selector to switch the C# and TypeScript examples throughout the page.
+If you're new to the PostgreSQL integration, start with the [Get started with PostgreSQL integrations](/integrations/databases/postgres/postgres-get-started/) guide. For how consuming apps read the connection information this page exposes, see [Connect to PostgreSQL](../postgres-connect/). For the PostgreSQL Entity Framework Core (EF Core) client integration, see [Get started with the PostgreSQL Entity Framework Core integrations](/integrations/databases/efcore/postgres/postgresql-get-started/).
## Installation
@@ -43,13 +32,17 @@ The PostgreSQL hosting integration models various PostgreSQL resources as the fo
To access these types and APIs for expressing them as resources in your [`AppHost`](/get-started/app-host/) project, install the [📦 Aspire.Hosting.PostgreSQL](https://www.nuget.org/packages/Aspire.Hosting.PostgreSQL) NuGet package:
-
-
+
+
```bash title="Terminal"
aspire add postgres
```
+
+ Learn more about [`aspire add`](/reference/cli/commands/aspire-add/) in the command reference.
+
+
Or, choose a manual installation approach:
```csharp title="C# — AppHost.cs"
@@ -61,12 +54,16 @@ Or, choose a manual installation approach:
```
-
+
```bash title="Terminal"
aspire add postgres
```
+
+ Learn more about [`aspire add`](/reference/cli/commands/aspire-add/) in the command reference.
+
+
This updates your `aspire.config.json` with the PostgreSQL hosting integration package:
```json title="aspire.config.json" ins={3}
@@ -84,8 +81,8 @@ This updates your `aspire.config.json` with the PostgreSQL hosting integration p
In your AppHost project, add a PostgreSQL server resource and then add a database resource as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -98,7 +95,7 @@ var exampleProject = builder.AddProject("apiservice")
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -127,12 +124,8 @@ await builder.addNodeApp("api", "./api", "index.js")
-
-
## Add PostgreSQL resource with database scripts
@@ -145,8 +138,8 @@ CREATE DATABASE ""
To alter the default script, configure the database resource with a custom creation script:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -169,7 +162,7 @@ builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -205,8 +198,8 @@ The preceding example creates a database named `app_db`. The script is run when
Add the [**dpage/pgadmin4**](https://www.pgadmin.org/) container to the PostgreSQL resource to get a web-based admin dashboard, as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -221,7 +214,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -246,8 +239,8 @@ The preceding code adds a container based on the `docker.io/dpage/pgadmin4` imag
To configure the host port for the pgAdmin container, configure the pgAdmin resource inside the callback as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -262,7 +255,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -291,8 +284,8 @@ The preceding code adds and configures the host port for the pgAdmin container.
Add the [**sosedoff/pgweb**](https://sosedoff.github.io/pgweb/) container to the PostgreSQL resource to get a web-based admin dashboard, as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -307,7 +300,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -332,8 +325,8 @@ The preceding code adds a container based on the `docker.io/sosedoff/pgweb` imag
To configure the host port for the pgWeb container, configure the pgWeb resource inside the callback as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -348,7 +341,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -377,8 +370,8 @@ The preceding code adds and configures the host port for the pgWeb container. Th
Add a data volume to the PostgreSQL server resource as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -393,7 +386,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -424,15 +417,15 @@ The data volume is used to persist the PostgreSQL server data outside the lifecy
Add a data bind mount to the PostgreSQL server resource as shown in the following examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres")
.WithDataBindMount(
- source: "/PostgreSQL/Data",
- isReadOnly: false);
+ source: "/PostgreSQL/Data",
+ isReadOnly: false);
var postgresdb = postgres.AddDatabase("postgresdb");
@@ -442,7 +435,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -471,8 +464,8 @@ Data bind mounts rely on the host machine's filesystem to persist the PostgreSQL
Use initialization files to seed the PostgreSQL server. The C# AppHost exposes `WithInitBindMount(...)`, while the TypeScript AppHost exposes `withInitFiles(...)`.
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -487,7 +480,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -514,8 +507,8 @@ The init bind mount relies on the host machine's filesystem to initialize the Po
When you want to explicitly provide the username and password used by the container image, you can provide these credentials as parameters. Consider the following alternative examples:
-
-
+
+
```csharp title="C# — AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);
@@ -531,7 +524,7 @@ var exampleProject = builder.AddProject()
// After adding all resources, run the app...
```
-
+
```typescript title="TypeScript — apphost.ts" twoslash
import { createBuilder } from './.modules/aspire.js';
@@ -551,11 +544,57 @@ await builder.addNodeApp("api", "./api", "index.js")
-### Connection properties
+## Pass custom environment variables
+
+By default, Aspire injects the PostgreSQL connection information using variable names derived from the resource name (for example, `POSTGRESDB_URI`, `POSTGRESDB_HOST`, `POSTGRESDB_PORT`). If your consuming app expects a different set of environment variable names, pass individual connection properties from the AppHost:
+
+
+
+```csharp title="C# — AppHost.cs"
+var builder = DistributedApplication.CreateBuilder(args);
+
+var postgres = builder.AddPostgres("postgres");
+var database = postgres.AddDatabase("myDatabase");
+
+var app = builder.AddExecutable("my-app", "node", "app.js", ".")
+ .WithReference(database)
+ .WithEnvironment(context =>
+ {
+ context.EnvironmentVariables["POSTGRES_HOST"] = postgres.Resource.PrimaryEndpoint.Property(EndpointProperty.Host);
+ context.EnvironmentVariables["POSTGRES_PORT"] = postgres.Resource.PrimaryEndpoint.Property(EndpointProperty.Port);
+ context.EnvironmentVariables["POSTGRES_USER"] = postgres.Resource.UserNameParameter;
+ context.EnvironmentVariables["POSTGRES_PASSWORD"] = postgres.Resource.PasswordParameter;
+ context.EnvironmentVariables["POSTGRES_DATABASE"] = database.Resource.DatabaseName;
+ });
+
+builder.Build().Run();
+```
+
+
+```typescript title="TypeScript — apphost.ts" twoslash
+import { createBuilder } from './.modules/aspire.js';
+
+const builder = await createBuilder();
+
+const postgres = await builder.addPostgres("postgres");
+const database = await postgres.addDatabase("myDatabase");
+
+await builder.addNodeApp("my-app", "./app", "index.js")
+ .withReference(database)
+ .withEnvironment("POSTGRES_USER", postgres.userNameParameter)
+ .withEnvironment("POSTGRES_PASSWORD", postgres.passwordParameter)
+ .withEnvironment("POSTGRES_DATABASE", "myDatabase");
+
+await builder.build().run();
+```
+
+
+
+## Connection properties
-For a full reference of PostgreSQL connection properties and environment variables — including how to connect from JavaScript, Python, Go, and .NET — see [Connect to PostgreSQL](../postgres-connect/).
+For the full reference of PostgreSQL connection properties — and how consuming apps in C#, TypeScript, Python, and Go read them — see [Connect to PostgreSQL](../postgres-connect/).
-### Hosting integration health checks
+## Hosting integration health checks
The PostgreSQL hosting integration automatically adds a health check for the PostgreSQL server resource. The health check verifies that the PostgreSQL server is running and that a connection can be established to it.
diff --git a/src/frontend/src/styles/site.css b/src/frontend/src/styles/site.css
index e02e05b26..735c095ca 100644
--- a/src/frontend/src/styles/site.css
+++ b/src/frontend/src/styles/site.css
@@ -291,6 +291,10 @@ aside {
margin-bottom: 1rem;
}
+.sl-steps:has(> li:first-child > div.sl-heading-wrapper) {
+ padding-top: 1rem;
+}
+
.sl-steps ul:not([role='tablist']) {
padding-bottom: 1rem;
}
diff --git a/src/frontend/tests/e2e/pivot-selector.spec.ts b/src/frontend/tests/e2e/pivot-selector.spec.ts
index b38c5c930..b0515a9ec 100644
--- a/src/frontend/tests/e2e/pivot-selector.spec.ts
+++ b/src/frontend/tests/e2e/pivot-selector.spec.ts
@@ -38,6 +38,66 @@ test('prerequisites apphost tabs switch visible content and persist selection',
await expect(csharpContent).toBeHidden();
});
+test('apphost tabs restore and sync the aspire-lang query string', async ({ page }) => {
+ await page.addInitScript(() => {
+ localStorage.setItem('aspire-lang', 'csharp');
+ localStorage.setItem('starlight-synced-tabs__aspire-lang', 'C#');
+ });
+
+ await page.goto('/get-started/prerequisites/?aspire-lang=typescript');
+ await dismissCookieConsentIfVisible(page);
+
+ const appHostTabs = page.locator('starlight-tabs[data-sync-key="aspire-lang"]').first();
+ const csharpTab = appHostTabs.getByRole('tab', { name: 'C#' });
+ const typeScriptTab = appHostTabs.getByRole('tab', { name: 'TypeScript' });
+ const csharpContent = page.getByText('The .NET 10.0 SDK is required for C# AppHosts', {
+ exact: false,
+ });
+ const typeScriptContent = page.getByRole('link', { name: 'Node.js installation instructions' });
+
+ await expect(page).toHaveURL(/\?aspire-lang=typescript$/);
+ await expect(typeScriptTab).toHaveAttribute('aria-selected', 'true');
+ await expect(csharpTab).toHaveAttribute('aria-selected', 'false');
+ await expect(typeScriptContent).toBeVisible();
+ await expect(csharpContent).toBeHidden();
+ await expect
+ .poll(() => page.evaluate(() => localStorage.getItem('aspire-lang')))
+ .toBe('typescript');
+ await expect
+ .poll(() => page.evaluate(() => localStorage.getItem('starlight-synced-tabs__aspire-lang')))
+ .toBe('TypeScript');
+ await expect
+ .poll(() => page.evaluate(() => document.documentElement.dataset.apphostLang))
+ .toBe('typescript');
+
+ await csharpTab.click();
+
+ await expect(page).toHaveURL(/\?aspire-lang=csharp$/);
+ await expect(csharpTab).toHaveAttribute('aria-selected', 'true');
+ await expect(typeScriptTab).toHaveAttribute('aria-selected', 'false');
+ await expect(csharpContent).toBeVisible();
+ await expect(typeScriptContent).toBeHidden();
+ await expect
+ .poll(() => page.evaluate(() => localStorage.getItem('aspire-lang')))
+ .toBe('csharp');
+});
+
+test('postgres apphost tabs use the shared aspire-lang query string', async ({ page }) => {
+ await page.goto('/integrations/databases/postgres/postgres-host/?aspire-lang=typescript');
+ await dismissCookieConsentIfVisible(page);
+
+ const appHostTabs = page.locator('starlight-tabs[data-sync-key="aspire-lang"]').first();
+ const csharpTab = appHostTabs.getByRole('tab', { name: 'C#' });
+ const typeScriptTab = appHostTabs.getByRole('tab', { name: 'TypeScript' });
+ const csharpPanel = appHostTabs.locator(':scope > [role="tabpanel"]').nth(0);
+ const typeScriptPanel = appHostTabs.locator(':scope > [role="tabpanel"]').nth(1);
+
+ await expect(typeScriptTab).toHaveAttribute('aria-selected', 'true');
+ await expect(csharpTab).toHaveAttribute('aria-selected', 'false');
+ await expect(typeScriptPanel).toBeVisible();
+ await expect(csharpPanel).toBeHidden();
+});
+
test('app host page restores pivot state from the lang query string', async ({ page }) => {
await page.goto('/get-started/app-host/?lang=nodejs');
await dismissCookieConsentIfVisible(page);