Generating Client SDKs with OpenAPI Generator CLI in an Aspire-Based Project
Working with OpenAPI Generator CLI should be a straightforward step in automating API client generation. However, integrating it with newer project structures like those built using Aspire, along with modern DevOps pipelines, reveals some hidden complexity. What follows is a breakdown of how I installed and configured the CLI, dealt with SSL certificate issues from local development servers, and struggled to streamline the process using Swashbuckle.AspNetCore.Cli.
Installation and Setup of OpenAPI Generator CLI
The OpenAPI Generator CLI is a Java-based tool designed to replace the older and now deprecated swagger-codegen. Unfortunately, my muscle memory kicked in when I first attempted to set this up, and I initially went down the familiar swagger-codegen path—reinstalling tooling and setting up JDK 8, only to realize that it was no longer compatible or maintained. This led to a rabbit hole of legacy dependencies, broken configurations, and incompatibility errors.
After identifying my mistake, I had to unwind the mess — removing old installations and deprecated tools. I then installed the correct setup using JDK 17, which is supported by the OpenAPI Generator CLI. Using Scoop made this process easier:
scoop install openjdk17
scoop reset openjdk17
With JAVA_HOME properly configured and java -version showing the right build, I was ready to start generating code using the CLI.
Self-Signed Certificate Issues with Aspire-Hosted Swagger
The next step was to generate a client from the Swagger JSON exposed by my locally running Aspire API service:
generate -i "https://localhost:7393/swagger/v1/swagger.json" -g csharp
I get this error:
[main] ERROR io.swagger.v3.parser.util.RemoteUrl — unable to read
javax.net.ssl.SSLHandshakeException: PKIX path building failed…
This pointed to a common SSL handshake issue — OpenAPI Generator doesn’t trust the self-signed certificate that Aspire uses for its HTTPS endpoints. As expected, the Java-based parser rejected the endpoint due to a missing trusted certification path.
Problems with SSL certificate · Issue #2929
I found several potential workarounds online, most of them recommending a trust-all setup via environment variables:
export JAVA_OPTS="-Dio.swagger.parser.util.RemoteUrl.trustAll=true -Dio.swagger.v3.parser.util.RemoteUrl.trustAll=true"
Unfortunately, none of these worked in my environment. The CLI continued to refuse the connection regardless of how I configured JAVA_OPTS.
Eventually, I gave up on hitting the hosted endpoint directly and manually downloaded the swagger.json file to my local filesystem. Once I did that, I was able to generate the client without any issues—as long as I used an absolute path, since relative paths seemed to break the generator:
openapi-generator-cli generate -i C:\path\to\swagger.json -g csharp
[Usage | OpenAPI Generator](https://openapi-generator.tech/docs/usage) |
Attempting a CI-Friendly Swagger Generation with Swashbuckle
Even though downloading the Swagger file manually was successful, I realized that for long-term maintainability, especially in a CI/CD workflow, I needed a way to generate both the Swagger JSON and the client SDK as part of a pull request. Ideally, this would happen automatically on pre-commit or in GitHub Actions.
To that end, I tried to use the Swashbuckle.AspNetCore.Cli tool to generate the Swagger file from my Aspire-based API project without relying on the service being up and running. First, I installed the CLI:
dotnet tool install --global Swashbuckle.AspNetCore.Cli
However, Aspire adds some complexity here. My project builds both a .dll and an .exe. I assumed the .exe was the entry point and tried using that instead.
So, I attempted to generate the Swagger file using the standard command:
swagger tofile --output swagger.json Dinkr.ApiService\bin\Debug\net8.0\Dinkr.ApiService.exe v1
``
When I run it on the EXE I get this:
A JSON parsing exception occurred in [C:\Users\markti\source\repos\dinkr\Dinkr.ApiService\bin\Debug\net8.0\Dinkr.ApiService.exe], offset 0 (line 1, column 1): Invalid val
ue.
A fatal error was encountered. The library ‘hostpolicy.dll’ required to execute the application was not found in ‘C:\Users\markti\source\repos\dinkr\Dinkr.ApiService\bin
Debug\net8.0\’.
Failed to run as a self-contained app.
— The application was run as a self-contained app because ‘C:\Users\markti\source\repos\dinkr\Dinkr.ApiService\bin\Debug\net8.0\Dinkr.ApiService.json’ was not found.
— If this should be a framework-dependent app, add the ‘C:\Users\markti\source\repos\dinkr\Dinkr.ApiService\bin\Debug\net8.0\Dinkr.ApiService.json’ file and specify the
appropriate framework.
This strongly hinted that the CLI was treating the .exe as a self-contained app but couldn’t locate the necessary runtime components.
Switching back to the .dll produced a different issue:
When I run it on the DLL I get this:
Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly ‘System.Runtime, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’. The system cannot find the file specified. File name: ‘System.Runtime, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ ```
At this point, I started to suspect that Aspire’s project structure, which emphasizes modularization and containerization, doesn’t play nicely with Swashbuckle.AspNetCore.Cli. It’s possible the CLI expects a more traditional web API project layout and struggles with Aspire’s more complex startup flow.
Final Thoughts
While I don’t yet have a complete solution for fully automating Swagger and SDK generation within an Aspire-based project, I’ve at least unblocked myself for local development by manually downloading the Swagger file and using absolute paths with the OpenAPI Generator CLI.
Still, my long-term goal is to have a smooth DevOps process: any time I update the API, a GitHub Action should regenerate the client SDK and publish a new NuGet package. This would keep my MAUI and Web frontends in sync with server-side changes. Given that the Web frontend is hosted in Aspire as well I would think Aspire would just take care of this for me — maybe it does and I just don’t know it?
The blocker now appears to be the compatibility (or lack thereof) between Aspire’s build output and the Swashbuckle CLI. The errors related to missing hostpolicy.dll and System.Runtime assemblies suggest a deeper issue in how Aspire apps are bundled. This of course wouldn’t be necessary (at least for the Web Client) if Aspire took care of this for me — which seems like something it should do given it boasts “service discovery”. However, it seems service service only means telling me what the URL is, the service client, I have to go figure out on my own. This definitely seems like a problem that the Application Layer should be solving. Infrastructure-as-Code long has solved the problem of piping configuration settings such as the current URL of the API to the Web Front End. Ironically, Aspire seems to function in the same way as Infrastructure-as-Code here — unless it keeps it updated during failover in disaster scenarios — but somehow I doubt it.
Whether this is a tooling limitation or something inherently quirky about Aspire’s project model remains unclear. But for now, it’s a frustrating gap in what otherwise feels like a very productive development environment.