An Aspire application whose API and playground are TypeScript. The AppHost is C#. It starts the App Configuration emulator, a SQL Server container, and the two Node processes. server.ts only builds the app and listens. It does not name a feature or open a database.
GET /hello returns Hello World. When the HelloNewWorld flag is on, it returns Hello New World. GET /ping returns { "status": "pong" }. Todos are created, listed, updated, and deleted in SQL Server. A blank title, or a title longer than 200 characters, is a 400 problem response and does not reach the database.
flowchart LR
web[Playground] -->|/api| api[Api]
api --> sql[(SQL)]
api --> cfg[App Configuration]
- One Fastify app. Tests call
buildAppandapp.inject. They do not start Aspire. - A feature is a folder.
loadFeaturesimports everyconfigure.tsand every file named*Endpoint.ts. The host has no list of features. Node has no assembly scan, so the convention is the file name. - Zod checks the body before any SQL. The store runs parameterized statements. The table script is
0001_create_todos.sql, recorded indbo.SchemaMigrations. Applying it takes a SQL application lock so two replicas cannot run the same script together. npm run devruns the API through tsx. The published container runsnpm run build, thennode dist/server.js. Relative imports in the API end in.jsso that output loads under plain Node. The SQL script is copied beside the compiled schema module.- The flag refreshes about every 5 seconds. No App Configuration connection means the flag is off. A failed refresh keeps the last value. The playground turns
HelloNewWorldon and off withPUT /hello/flag. That writes the flag and bumpsDemo:Sentinel. The next hello call sees it after the refresh. - The playground is a second process. It proxies
/api/*to the API address Aspire injects, and the browser stays on one origin.
hosting/features.ts loads every feature. A feature folder adds configure.ts when it has services, and one *Endpoint.ts per action. GetHelloEndpoint reads the flag. CreateTodoEndpoint checks the title and calls the store. The todos configure.ts opens SQL and applies 0001_create_todos.sql. Delete the folder and both the routes and the setup are gone.
export function endpoint(services: Services) {
const store = services.get(todoStore);
return async function register(app: FastifyInstance): Promise<void> {
app.post("/todos", async (request, reply) => {
const parsed = createTodo.safeParse(request.body);
if (!parsed.success) {
return sendProblem(reply, validationProblem(parsed.error));
}
const todo = await store.create(parsed.data.title);
return reply.code(201).header("location", `/todos/${todo.id}`).send(todo);
});
};
}The title rule lives next to the row type in features/todos/todo.ts. The store in todo-store.ts does not validate. The route does not write SQL.
The SQL connection name is todos. Aspire injects an ADO.NET string (Server=...;User ID=...;Password=...;Database=todos). openPool turns that into the mssql config, including Authentication=Active Directory Default for a published Azure SQL database. Locally the server is a container and the password is Aspire's.
The App Configuration connection name is appconfiguration. An http:// endpoint is the emulator, read with the key-value HTTP API. An https:// endpoint uses @azure/app-configuration with the connection string, or DefaultAzureCredential when the string has no secret. The emulator seeder writes HelloNewWorld disabled and Demo:Sentinel set to 1. The flag document is .appconfig.featureflag/HelloNewWorld.
dotnet run --project src/AppHostDocker must be running. The first start pulls the App Configuration emulator and SQL Server. The playground listens on http://localhost:5190. The dashboard lists the same address on the web resource.
npm testTodo tests start one SQL Server Testcontainer and call the real routes. Loading the app runs every feature setup, so hello and ping tests use that same database. Nothing in the test project starts the AppHost.
| Path | Owns |
|---|---|
src/AppHost |
Aspire. Emulator, SQL, and the two Node apps. C#. |
src/api |
Fastify. Hello, ping, todos, SQL, feature flags. |
src/web |
The playground and the /api proxy. |