Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/content/docs/graphql/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ You need the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). [
dotnet run --project ./src/Presentation
```

It answers on `https://localhost:8081`. If your browser or `curl` doesn't trust the .NET development certificate yet, run `dotnet dev-certs https --trust` once.
It answers on `https://localhost:7331`. If your browser or `curl` doesn't trust the .NET development certificate yet, run `dotnet dev-certs https --trust` once.

5. Open Nitro, the GraphQL IDE, at [https://localhost:8081/graphql](https://localhost:8081/graphql). The schema is on the left, with every type and field described, and the editor autocompletes as you type. Paste this in and press play:
5. Open Nitro, the GraphQL IDE, at [https://localhost:7331/graphql](https://localhost:7331/graphql). The schema is on the left, with every type and field described, and the editor autocompletes as you type. Paste this in and press play:

```graphql
{
Expand All @@ -74,7 +74,7 @@ You need the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). [
6. Or ask from the terminal.

```bash
curl -X POST "https://localhost:8081/graphql" -H "Content-Type: application/json" \
curl -X POST "https://localhost:7331/graphql" -H "Content-Type: application/json" \
-d '{"query":"{ movies(query: \"space\", first: 2) { totalCount nodes { id title reviewCount averageStars } } }"}'
```

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/graphql/guides/add-a-field.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ This guide adds `bestReview` to the `Movie` type: the highest rated review of th

```bash
dotnet run --project ./src/Presentation
curl "https://localhost:8081/graphql?sdl" > tests/Presentation.Tests.Integration/GraphQL/Schema/schema.graphql
curl "https://localhost:7331/graphql?sdl" > tests/Presentation.Tests.Integration/GraphQL/Schema/schema.graphql
```

The diff is the one field, described:
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/graphql/guides/database.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The next start builds a fresh database and seeds it again.
Set `ConnectionStrings__MovieReviews`. To keep everything in memory, which is what the tests do:

```bash
docker run --rm -p 8080:8080 -e DOTNET_ENVIRONMENT=docker \
docker run --rm -p 7330:7330 -e DOTNET_ENVIRONMENT=docker \
-e ConnectionStrings__MovieReviews="Data Source=MovieReviews;Mode=Memory;Cache=Shared" \
acme-movies:local
```
Expand Down
14 changes: 7 additions & 7 deletions src/content/docs/graphql/guides/run-locally.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ There are two ready-made environments. `local` is for `dotnet run`: sample data
dotnet run --project ./src/Presentation
```

`launchSettings.json` sets the environment to `local` and listens on [https://localhost:8081](https://localhost:8081), and on `https://localhost:8081`, which redirects to it. If your browser or `curl` doesn't trust the .NET development certificate yet, run `dotnet dev-certs https --trust` once.
`launchSettings.json` sets the environment to `local` and listens on [https://localhost:7331](https://localhost:7331), and on `https://localhost:7331`, which redirects to it. If your browser or `curl` doesn't trust the .NET development certificate yet, run `dotnet dev-certs https --trust` once.
</TabItem>
<TabItem label="Docker Compose">
```bash
docker compose up -d --build
```

Compose builds the `run` stage, starts an OpenTelemetry Collector and the Aspire dashboard next to it, and publishes the API on [http://localhost:8080](http://localhost:8080). Shut it down with `docker compose down`, or `docker compose down -v` to drop the database too.
Compose builds the `run` stage, starts an OpenTelemetry Collector and the Aspire dashboard next to it, and publishes the API on [http://localhost:7330](http://localhost:7330). Shut it down with `docker compose down`, or `docker compose down -v` to drop the database too.
</TabItem>
<TabItem label="Docker">
```bash
docker build . --target run --tag acme-movies:local
docker run --rm -p 8080:8080 -e DOTNET_ENVIRONMENT=docker acme-movies:local
docker run --rm -p 7330:7330 -e DOTNET_ENVIRONMENT=docker acme-movies:local
```

Without Compose there's no collector, so the logs stay on the console and nothing else is exported. Set `OTEL_EXPORTER_OTLP_ENDPOINT` to send them to a collector of your own.
Expand All @@ -44,19 +44,19 @@ There are two ready-made environments. `local` is for `dotnet run`: sample data
| `/graphql?sdl` | The schema in SDL, the same text the snapshot test checks |
| `/health/live` | Answers 200 while the process is up |
| `/health/ready` | Answers 200 when the database can be reached |
| [http://localhost:18888](http://localhost:18888) | The Aspire dashboard, with the logs, traces and metrics, when running with Docker Compose |
| [http://localhost:7332](http://localhost:7332) | The Aspire dashboard, with the logs, traces and metrics, when running with Docker Compose |

The health endpoints answer without a token even when authentication is on, so a load balancer never needs to log in.

## See the telemetry

Docker Compose runs an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) beside the API, and the collector forwards everything to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:18888](http://localhost:18888). Open **Traces** to follow a request from the HTTP request through the GraphQL operation to the use case, **Structured** for the logs, each linked to its trace, and **Metrics** for request durations, EF Core queries and the runtime. The dashboard keeps everything in memory, so it's empty again after a restart. The dashboard has no login, and the collector accepts OTLP from anyone on ports 4317 and 4318. That's on purpose, so there's nothing to sign in to on your machine, and it's why the compose file is for your machine only. Don't run it anywhere other people can reach.
Docker Compose runs an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) beside the API, and the collector forwards everything to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:7332](http://localhost:7332). Open **Traces** to follow a request from the HTTP request through the GraphQL operation to the use case, **Structured** for the logs, each linked to its trace, and **Metrics** for request durations, EF Core queries and the runtime. The dashboard keeps everything in memory, so it's empty again after a restart. The dashboard has no login, and the collector accepts OTLP from anyone on ports 7333 and 7334. That's on purpose, so there's nothing to sign in to on your machine, and it's why the compose file is for your machine only. Don't run it anywhere other people can reach.

The API only ever talks OTLP to the collector, and `otel-collector.yaml` decides where things go. To send them to a real backend, add its exporter there; the API doesn't change. To see a `dotnet run` in the dashboard, start the collector and point the API at it:

```bash
docker compose up -d otel-collector
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 dotnet run --project ./src/Presentation
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:7333 dotnet run --project ./src/Presentation
```

## Try the schema
Expand Down Expand Up @@ -117,5 +117,5 @@ docker build . --target run --tag acme-movies:latest \
The `version` query then reports `1.2.3-beta` and the commit it was built from.

<Aside type="tip" title="Stuck?">
If Nitro loads but can't reach the schema, you're probably on `https://` with a certificate your browser doesn't trust yet. Run `dotnet dev-certs https --trust` once and reload. The `http://` address only redirects to `https://`, so it doesn't help. If `curl` says connection refused, check that ports 8080 and 8081 aren't busy with something else you forgot about.
If Nitro loads but can't reach the schema, you're probably on `https://` with a certificate your browser doesn't trust yet. Run `dotnet dev-certs https --trust` once and reload. The `http://` address only redirects to `https://`, so it doesn't help. If `curl` says connection refused, check that ports 7330 and 7331 aren't busy with something else you forgot about.
</Aside>
2 changes: 1 addition & 1 deletion src/content/docs/graphql/guides/secure-the-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Out of the box the API runs wide open, and there's no authentication code in it
2. Get a token from the authority for that audience, and send it.

```bash
curl -X POST "https://localhost:8081/graphql" -H "Authorization: Bearer eyJhbGciOi..." \
curl -X POST "https://localhost:7331/graphql" -H "Authorization: Bearer eyJhbGciOi..." \
-H "Content-Type: application/json" -d '{"query":"{ movies { totalCount } }"}'
```

Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/graphql/guides/ship-the-container.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar:

import { Aside } from '@astrojs/starlight/components';

The `run` stage of the `Dockerfile` is the whole API: one image, built on `mcr.microsoft.com/dotnet/aspnet:10.0-alpine`, that listens on port 8080 and runs as the image's non-root `app` user. It runs anywhere a container runs, which these days is everywhere, including a surprising number of fridges.
The `run` stage of the `Dockerfile` is the whole API: one image, built on `mcr.microsoft.com/dotnet/aspnet:10.0-alpine`, that listens on port 7330 and runs as the image's non-root `app` user. It runs anywhere a container runs, which these days is everywhere, including a surprising number of fridges.

## Build it

Expand Down Expand Up @@ -58,5 +58,5 @@ The SQLite file lives in `/app/data`. Mount a volume there or, better, [point In
In a solution created with `--include-auth`, set `Authentication__Authority` and `Authentication__Audience` as environment variables to point each environment at its own authorization server. Without `--include-auth` there's nothing to turn on yet. The [security guide](../secure-the-api/) has the details.

<Aside type="tip" title="Where to run it">
Azure Container Apps, AWS App Runner and ECS, Google Cloud Run, Fly.io, Kubernetes, or a Raspberry Pi under the desk. The image doesn't care. It only wants port 8080, a `DOTNET_ENVIRONMENT`, and to be left alone.
Azure Container Apps, AWS App Runner and ECS, Google Cloud Run, Fly.io, Kubernetes, or a Raspberry Pi under the desk. The image doesn't care. It only wants port 7330 (point the platform's target port at it), a `DOTNET_ENVIRONMENT`, and to be left alone.
</Aside>
2 changes: 1 addition & 1 deletion src/content/docs/graphql/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ cd WorksOnMyMachine.Movies
docker compose up -d
```

Then open Nitro, the GraphQL IDE, at [http://localhost:8080/graphql](http://localhost:8080/graphql) and start querying. The logs, traces and metrics go to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:18888](http://localhost:18888), because `Console.WriteLine` is not an observability strategy.
Then open Nitro, the GraphQL IDE, at [http://localhost:7330/graphql](http://localhost:7330/graphql) and start querying. The logs, traces and metrics go to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:7332](http://localhost:7332), because `Console.WriteLine` is not an observability strategy.

Want it locked down? Add `--include-auth`, `--authority` and `--audience` when you create the solution and the movie, author and review queries and the review mutations need a JWT bearer token. Leave `--include-auth` off and there's no auth code at all, which is lovely on a laptop and less lovely in production.

Expand Down
12 changes: 7 additions & 5 deletions src/content/docs/graphql/reference/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Every setting in `appsettings.json` can be overridden with an environment variab
| Variable | Default | Description |
| --- | --- | --- |
| `DOTNET_ENVIRONMENT` | none | Layers `appsettings.<environment>.json` over `appsettings.json`. The template ships `local` and `docker` |
| `ASPNETCORE_URLS` | `http://+:8080` in the container | The addresses to listen on |
| `ASPNETCORE_HTTP_PORTS` | `7330` in the container | The port to listen on |
| `ASPNETCORE_FORWARDEDHEADERS_ENABLED` | none | Set to `true` behind a proxy that ends TLS, so HTTPS redirection sees the original scheme |
| `ConnectionStrings__MovieReviews` | `Data Source=movie-reviews.db` | The SQLite connection string. `appsettings.docker.json` points it at `/app/data/movie-reviews.db` |
| `Database__SeedSampleData` | `false` | When `true`, seeds sample data into an empty database. On in `local` and `docker` |
Expand All @@ -34,9 +34,11 @@ Every setting in `appsettings.json` can be overridden with an environment variab

| Service | What it is | Port |
| --- | --- | --- |
| `api` | The image, built from the `run` stage | 8080 |
| `otel-collector` | The [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/), configured by `otel-collector.yaml` | 4317 (gRPC), 4318 (HTTP) |
| `aspire-dashboard` | The [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone), where the collector sends everything | 18888 |
| `api` | The image, built from the `run` stage | 7330, inside the container too |
| `otel-collector` | The [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/), configured by `otel-collector.yaml` | 7333 (gRPC), 7334 (HTTP) |
| `aspire-dashboard` | The [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone), where the collector sends everything | 7332 |

The ports are the ones on your machine. Each template has its own block of ten, so this one uses 7330 to 7339 and runs beside a solution made from any other template: Minimal API 7310, MCP 7320, GraphQL 7330, Web API 7340 and Lambda Function 7350. The app uses the same port inside its container; the dashboard and collector keep their usual ones.

The `api` service runs with `DOTNET_ENVIRONMENT=docker`, mounts the `movie-reviews-data` volume at `/app/data`, and sends its logs, traces and metrics over OTLP to `otel-collector`, which forwards them to `aspire-dashboard`.

Expand All @@ -55,4 +57,4 @@ Every span, metric and log also carries `service.version`, the version stamped i

## Ports when running with dotnet run

`src/Presentation/Properties/launchSettings.json` sets `DOTNET_ENVIRONMENT=local` and listens on `http://localhost:8080` and `https://localhost:8081`.
`src/Presentation/Properties/launchSettings.json` sets `DOTNET_ENVIRONMENT=local` and listens on `http://localhost:7330` and `https://localhost:7331`.
4 changes: 2 additions & 2 deletions src/content/docs/lambda/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ You need the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) an
docker compose up -d --build
```

5. Invoke the function. The emulator listens on port 9000 and answers the same way Lambda does.
5. Invoke the function. The emulator listens on port 7350 and answers the same way Lambda does.

```bash
curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
curl -X POST "http://localhost:7350/2015-03-31/functions/function/invocations" \
-d '{"query":"space","limit":2}'
```

Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/lambda/guides/add-a-function.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ This guide adds a `GetMovie` function that returns one movie with all of its rev

```bash
docker build . --target run --tag acme-reviews:local
docker run --rm -p 9000:8080 -e Database__SeedSampleData=true acme-reviews:local \
docker run --rm -p 7350:8080 -e Database__SeedSampleData=true acme-reviews:local \
"Presentation::WorksOnMyMachine.Reviews.Presentation.LambdaFunction.Handler_GetMovie_Generated::GetMovie"

curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
curl -X POST "http://localhost:7350/2015-03-31/functions/function/invocations" \
-d '{"id":"cb188ef6-14e7-f85f-2f78-924c2ab26b28"}'
```

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/lambda/guides/database.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Every time a container starts, the handler applies any pending Entity Framework
Set `ConnectionStrings__MovieReviews`. To keep everything in memory:

```bash
docker run --rm -p 9000:8080 \
docker run --rm -p 7350:8080 \
-e ConnectionStrings__MovieReviews="Data Source=MovieReviews;Mode=Memory;Cache=Shared" \
-e Database__SeedSampleData=true \
acme-reviews:local
Expand Down
14 changes: 7 additions & 7 deletions src/content/docs/lambda/guides/run-locally.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ The image is built on the [AWS Lambda .NET base image](https://gallery.ecr.aws/l
docker compose up -d --build
```

Compose builds the `run` stage, publishes port 9000, turns on sample data, and starts an OpenTelemetry Collector and the Aspire dashboard next to it. Shut it down with `docker compose down`.
Compose builds the `run` stage, publishes port 7350, turns on sample data, and starts an OpenTelemetry Collector and the Aspire dashboard next to it. Shut it down with `docker compose down`.
</TabItem>
<TabItem label="Docker">
```bash
docker build . --target run --tag acme-reviews:local
docker run --rm -p 9000:8080 -e Database__SeedSampleData=true acme-reviews:local
docker run --rm -p 7350:8080 -e Database__SeedSampleData=true acme-reviews:local
```
</TabItem>
</Tabs>

Then invoke it from another terminal:

```bash
curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"limit":5}'
curl -X POST "http://localhost:7350/2015-03-31/functions/function/invocations" -d '{"limit":5}'
```

## See the telemetry

With Docker Compose, the function sends its traces, metrics and logs over OTLP to an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/), which forwards them to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:18888](http://localhost:18888). Each invocation is one trace: the function's span from the OpenTelemetry Lambda wrapper, with the use case under it. Traces and metrics are flushed before the function returns, so they're there by the time `curl` is. The logs still go to the console too, so `docker compose logs function` works as it always did. The dashboard has no login, and the collector accepts OTLP from anyone on ports 4317 and 4318. That's on purpose, so there's nothing to sign in to on your machine, and it's why the compose file is for your machine only. Don't run it anywhere other people can reach.
With Docker Compose, the function sends its traces, metrics and logs over OTLP to an [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/), which forwards them to the [Aspire dashboard](https://learn.microsoft.com/dotnet/aspire/fundamentals/dashboard/standalone) at [http://localhost:7352](http://localhost:7352). Each invocation is one trace: the function's span from the OpenTelemetry Lambda wrapper, with the use case under it. Traces and metrics are flushed before the function returns, so they're there by the time `curl` is. The logs still go to the console too, so `docker compose logs function` works as it always did. The dashboard has no login, and the collector accepts OTLP from anyone on ports 7353 and 7354. That's on purpose, so there's nothing to sign in to on your machine, and it's why the compose file is for your machine only. Don't run it anywhere other people can reach.

Run the image on its own and nothing is exported, because `OTEL_EXPORTER_OTLP_ENDPOINT` isn't set. Set it to send the telemetry to a collector of your own.

Expand All @@ -44,10 +44,10 @@ Run the image on its own and nothing is exported, because `OTEL_EXPORTER_OTLP_EN
The image runs `GetMovies` by default. The same image also has `GetAuthors` and `GetReviews`. Pass the function's full handler name to `docker run` to start a different one.

```bash
docker run --rm -p 9000:8080 -e Database__SeedSampleData=true acme-reviews:local \
docker run --rm -p 7350:8080 -e Database__SeedSampleData=true acme-reviews:local \
"Presentation::WorksOnMyMachine.Reviews.Presentation.LambdaFunction.Handler_GetReviews_Generated::GetReviews"

curl -X POST "http://localhost:9000/2015-03-31/functions/function/invocations" \
curl -X POST "http://localhost:7350/2015-03-31/functions/function/invocations" \
-d '{"movieId":"cb188ef6-14e7-f85f-2f78-924c2ab26b28","limit":2}'
```

Expand Down Expand Up @@ -85,5 +85,5 @@ docker build . --target run --tag acme-reviews:latest \
```

<Aside type="tip" title="Stuck?">
If `curl` hangs, the container is probably still applying migrations. Give it a second. If it says connection refused, check that port 9000 isn't busy with something else you forgot about.
If `curl` hangs, the container is probably still applying migrations. Give it a second. If it says connection refused, check that port 7350 isn't busy with something else you forgot about.
</Aside>
Loading
Loading