EcommerceDDD++ is a companion series to the original six-part series — each post covers a meaningful improvement or addition to the EcommerceDDD project that didn’t fit neatly into the original arc.
This first entry highlights automated API client generation: replacing verbose, manual HttpClient requests with strongly typed, SDK-style clients generated directly from the project’s OpenAPI specs.
I’ll walk through the rationale behind this shift, the choice of tools (Kiota and Koalesce), and how together they solve real-world integration challenges in a microservice architecture.
Why Generating API clients?
Manually invoking HTTP endpoints with HttpClient, string-based URIs, and handcrafted request/response models always felt error-prone and redundant, especially when both ends of the wire are within my control.
Leveraging existing OpenAPI definitions, I wanted a safer, less redundant, and more expressive approach. Code generation provides exactly that.
Kiota
Kiota is a Microsoft open-source CLI tool that generates strongly typed API clients from OpenAPI definitions. Instead of writing HTTP calls by hand, you point it at a spec and get a ready-to-use SDK — with full IntelliSense support and compile-time safety — in your language of choice.
Kiota helps by:
- Eliminating boilerplate HttpClient calls.
- Ensuring compile-time safety for routes, parameters, and data models.
- Reducing redundant DTOs, ViewModels, and manual object matching.
- Aligning SDK usage with your domain naming and semantics.
- Enhancing developer experience via IntelliSense and clean APIs.
With Kiota, consuming an endpoint like PUT /quotes/{quoteId}/items in TypeScript becomes a clean and intuitive one-liner:
Frontend (Angular/Typescript)
1
await quotesClient.api.quotes.byQuoteId(quoteId).items.put(request);
Backend (C#)
1
await _quotesClient.Api.Quotes[quoteId].Items.PutAsync(request);
No more manual path strings, header management, serialization logic, or model mismatches — the client handles all of it.
Koalesce

For backend service-to-service calls (East-West), generating individual Kiota clients per service is the right approach — each service consumes only the contract it directly depends on, with no Gateway in between. This is precisely how EcommerceDDD handles internal communication, using Docker tooling to generate targeted clients from each microservice’s own OpenAPI spec.
The frontend could follow the same pattern. Kiota itself even supports --include-path and --exclude-path flags, making it possible to filter out endpoints when generating from an individual service:
1
2
3
4
5
6
kiota generate \
--openapi http://orders-service/swagger.json \
--include-path "/orders/**" \
--exclude-path "/orders/internal/**" \
--language TypeScript \
--output ./src/app/clients/orders
So why not take that route for the frontend too? Three structural reasons:
1. The Gateway is the authoritative external contract. The Gateway defines what paths consumers actually call — including any route rewriting, prefix stripping, or access rules Ocelot applies. Individual microservice specs describe internal paths, which commonly differ from what’s externally routed, particularly when Ocelot rewrites or prefixes routes. Even when paths happen to match, generating a frontend client from individual service specs still produces something that reflects each service’s own view of its API — not the unified, curated surface the Gateway intentionally exposes. The only reliable source of truth for the external contract is the Gateway itself.
2. It leaks internal topology to the SPA. Generating N clients — one per microservice — means the frontend has to know which services exist, what their URIs are, and which endpoints belong to each. This violates the Gateway’s fundamental purpose: presenting a single, stable entry point while keeping the distributed backend opaque to consumers.
3. Kiota’s path filters are decoupled from Gateway configuration. What Ocelot exposes is defined in the Gateway’s routing configuration. What Kiota includes or excludes would be maintained separately — and these two can silently diverge. An endpoint you believe you’ve excluded might still appear in the generated client, or a newly added Gateway route may never make it into the spec. The filtering becomes a manual approximation of the actual security boundary, not a reflection of it.
What was really needed was a merged spec derived directly from the Gateway’s routing configuration — one that reflects the actual external contract, not an approximation built on top of internal specs.
That’s where the development challenge began. As we saw in Part 5 - Wrapping up infrastructure (Docker, API gateway, IdentityServer, Kafka), Ocelot aggregates microservices into a single entry point but doesn’t merge or expose a unified OpenAPI definition — a gap that’s common across most API Gateway solutions.
But what if we could merge all individual specs into one unified definition, scoped exactly to what the Gateway routes — accurate for documentation, auditing, and typed client generation alike?
That’s precisely the problem Koalesce solves. After searching for .NET tools that could do this seamlessly and finding none, I built Koalesce — a lightweight open-source .NET library for merging, sanitizing, and unifying multiple OpenAPI documents into a single coherent definition. In this context, it’s driven by the Gateway’s routing configuration to produce the authoritative external spec — which Kiota then uses to generate the frontend client.
Putting It All Together
- Each microservice holds its own OpenAPI definition.
- Koalesce merges them into a single spec scoped to the API Gateway routes — only what’s intentionally exposed externally (North-South). Internal-only endpoints never appear in the merged spec, and therefore never end up in the generated client.
- Kiota generates a single TypeScript client for the frontend and individual C# clients for backend service-to-service calls.
- The backend uses targeted Kiota clients per service for East-West communication — directly, without going through the Gateway.
- The frontend consumes all APIs through one Gateway-scoped Kiota client, entirely unaware of the microservice topology behind it.
⚠️ Worth noting: This approach works particularly well when you own and control all the services behind the Gateway — as is the case in EcommerceDDD. In a Backend for Frontend (BFF) architecture like this, where a single SPA is the primary consumer and all services are part of the same bounded system, Koalesce + Kiota provides a tight, compile-time contract between frontend and backend. If your Gateway proxies third-party or externally maintained services, the feasibility depends on whether those services expose reliable OpenAPI specs.
Hands-on
Generating a Single OpenAPI Definition with Koalesce
According to its README file, Koalesce simply needs URIs for OpenAPI specs, typically configured in appsettings.json:
With Koalesce, you can merge OpenAPI definitions in two ways:
Middleware approach (dynamic merge)
Add the Koalesce middleware to your .NET application pipeline (e.g., Program.cs) to expose a merged document on-the-fly at runtime.
✅ Pros
- Always up-to-date; changes reflect immediately.
- Speeds development; no manual regen or CI/CD needed.
❌ Cons
- Dependent on live services; failure in a microservice skips its definition.
- Can slow startup and Swagger load times.
- Misalignments between microservice specs (e.g. incompatible OpenAPI versions) can cause runtime errors.
CLI (Command Line Interface) approach
Use the Koalesce.CLI tool to output a .json or .yaml file containing the merged spec on the hard drive.
✅ Pros
- Because you run it manually, and when you know the services are available, the risk of runtime merging failure is reduced.
- Once merged, the single spec can be served without depending on microservices being online (great for CI/CD or sandbox environments).
❌ Cons
- Requires explicit regeneration on API changes.
- Risk of stale specs without automation.
My Approach to the EcommerceDDD project
I always test endpoints before having a UI, either through Swagger UI or Postman. While developing EcommerceDDD, I used Koalesce to merge the Gateway endpoints I wanted to expose for frontend consumption. Having all services accessible and documented from a single Swagger UI proved genuinely handy during development.
I went with the Middleware approach: the Koalesce NuGet package is installed directly in EcommerceDDD.ApiGateway, and wired into the pipeline in Program.cs like this:
Middleware (for API Gateway in active development):
1
2
3
4
5
6
7
8
9
10
11
12
// Register Koalesce
services.AddKoalesce(builder.Configuration)
.ForOpenAPI();
...
// Enable Koalesce before Swagger Middleware
app.UseKoalesce();
...
// Enable Swagger UI
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint(koalesceOptions.MergedOpenApiPath, koalesceOptions.Title);
});
Then, the ApiGateway now exposes a Swagger UI at http://localhost:5000/swagger/index.html like this:

Pretty cool, right? And since the frontend only knows the Gateway server URI, generating Kiota TypeScript clients directly into the SPA was a one-liner command:
Install Kiota as .NET tool
1
dotnet tool install --global Microsoft.OpenApi.Kiota
Generate Kiota client from the API Gateway OpenAPI definition
1
kiota generate --openapi http://localhost:5000/swagger/v2/apigateway.yaml --language TypeScript --output ./src/app/clients
That’s the raw command — useful to understand what happens under the hood. In practice, EcommerceDDD wraps this in Docker Compose tool services so regeneration is repeatable and doesn’t require a local Kiota installation.
Regenerating Clients in EcommerceDDD
To streamline the process, tool services are defined in docker-compose.override.yml under a dedicated tools profile. With the backend stack running, client regeneration is a single command:
1
2
3
4
5
6
# Regenerate both frontend and backend clients
docker compose --profile tools run regenerate-clients
# Or individually:
docker compose --profile tools run regenerate-frontend-clients # TypeScript (SPA)
docker compose --profile tools run regenerate-backend-clients # C# (microservices)
| Command | Output |
|---|---|
regenerate-frontend-clients | TypeScript client → EcommerceDDD.Spa/src/app/clients/ |
regenerate-backend-clients | C# clients → each service’s ServiceClients/Kiota/ folder |
regenerate-clients | Both of the above |
Each tool service points Kiota at the right spec source: the frontend client is generated from the Gateway’s merged Koalesce spec, while backend clients are generated individually from each microservice’s own OpenAPI definition — keeping East-West and North-South generation cleanly separated.
Final Thoughts
Kiota delivers real, day-to-day benefits: no more manually maintained HTTP calls, no model drift between frontend and backend, and a much tighter feedback loop when API contracts change. The generated client becomes a compile-time contract — if a route or type changes, the build tells you before runtime does.
Koalesce addresses a problem that runs deeper than spec fragmentation. The Gateway is the authoritative external contract — individual microservice specs, even when paths happen to match, reflect each service’s internal view rather than the curated surface the Gateway intentionally exposes.
Generating clients from those specs means maintaining a separate approximation of what the Gateway already defines, with the added risk of silent drift. Koalesce derives the merged spec directly from the Gateway’s routing configuration, making client generation a structural reflection of the actual external contract. East-West clients, meanwhile, remain targeted and independent — generated per service, without going through the Gateway.
That said, this approach has a cost. It introduces a regeneration step into your workflow, and a stale spec can give a false sense of safety. Automating regeneration in CI/CD — or at minimum gating it as a pre-build step — is what makes the full benefit stick.
Koalesce is still evolving and open to contributions. If you run into edge cases or have ideas for improvement, feedback is welcome.