OpenAPI Generator allows generation of API client libraries from OpenAPI Specs
github.com
github.com
The more time I spent with it the more disappointed I became. The reason why you are forced to write Java and write your own generator alongside the rest of the project is unclear to me. Extending the mustache templates is a pain (and handlebars experimental). Every little change you want to make requires you to change the library itself. Most generators I looked at (TS clients, Go clients and servers, Python clients) generated dubious code and project structures for actual usage.
My current approach to deal with all of this e.g. in Go server generation is to output to some temp directory and have (long) post generation scripts parse the current and generated AST to (1) restructure to my liking, (2) merge changes where appropriate (the generator would overwrite your code otherwise) and (3) generate helper files with Go templates, which are actually pleasant to use. (2) is necessary if you don't want to have generated maps all over the place to access the handler for this route, the middleware for this other one, etc. You won't have to edit generated files, but good luck figuring out what's going on. As long as you clearly state what methods can be edited and which ones are always replaced with the generator output you should be fine with (2).
You may be thinking:
> why do all of this instead of writing your own generator?
to which I answer: Touché.
We had a similar story – after looking at the OpenAPI Generator and other options, we ended up making our own in JS from the ground up, more or less (it used the internals of prettier to generate pretty code, especially example snippets for docs, across languages).
I'm now starting a company (https://stainlessapi.com, mentioned by OP elsewhere on this thread) to solve this problem more generally. If you ever get tired of maintaining this system (which may not happen, it does sound fun/interesting) or want broader language support, and are open to a commercial solution, drop me a line: alex at stainlessapi.com
(open invite of course – and also always happy to just chat codegen things, it's a neat little niche)
Based on experiences with the same generator at Stripe that Alex (founder of stainlessapi.com) built.
We've been building client sdks as a service at Speakeasy (https://www.speakeasyapi.dev/). API developer experience today in REST struggles because the openapi generators are buggy and generate machine-readable stubs rather than ergonomic SDKs. We ended up building a new generator from ground up focused on ensuring we could capture idiomatic features specific to each languages. For API consumers our goal is to provide a best in class dev ex and taking the burden off of having to rewrite common boilerplate like pagination and retries. For API producers you get to focus on building your API and not have to worry about table stakes features for your users. We started a closed beta programme recently. Shoot me a message at sagar@speakeasyapi.dev or come chat with us on slack if you want to early access!
https://join.slack.com/t/speakeasy-dev/shared_invite/zt-1df0...
Would love to know if anyone else has any language specific SDK generators they'd recommend, similar to the Deepmap implementation, but for another language (ruby, python, c#, java, etc).
OpenAPI Spec and auto-generated API clients are very useful when multiple languages need to be supported, like when running a developer program. I've worked at companies that both use OpenAPI Generator for official clients and ones that wrote our own tools for API client SDK generation (with different design philosophy). I've used a number of generators myself to compare and submitted fixes / enhancements to OpenAPI Generator. I used the Go client generator a while back and compared it to others, and recently started using the Crystal one.
To get the most the project, the following is useful: (a) need to support multiple languages, (b) ability to update the generator's code, both in Java and templates (Mustache or Handlebars), and (c) ability to discuss design in GitHub issues and the Slack channel.
The nice thing about OpenAPI Spec is that there is an ecosystem of tooling to support it, including rendering API references (HTML and PDF), API explorers (HTML pages to execute API calls), API clients, etc. But there is a learning curve. For writing specs by hand, I use and favor the Stoplight Studio IDE ( https://stoplight.io/studio ). For programmatically analyzing and editing specs, which is especially useful for finalizing auto-generated specs, I've built an OpenAPI Spec SDK library to make this easier ( https://github.com/grokify/spectrum ).
First, thanks for this. It's a lot of work to support OSS and I appreciate the effort of you and the team.
Second, is there any way for new users to determine the maturity of each of the (many) generators available? I haven't seen it in the documentation or the github repo, but maybe I missed it?
It does, but the generated code can be very shitty for some combinations of spec and output language. I maintain Rust bindings for the Kubernetes API server's API, and I chose to write my own code generator instead. The README at https://github.com/Arnavion/k8s-openapi has more details.
The point there about bugs in the spec is not limited to Kubernetes. $dayjob's software product interacts with the Docker daemon's server API so we generated bindings using the Docker spec, but that spec had a bug where some timespan values used the wrong integer type ( https://github.com/moby/moby/issues/39131 ). Luckily we were able to fix them in our generated code because we'd opted to check in the generated code instead of building it dynamically at compile time. The generated code also had a bug in the function for pulling Docker images, because it did not account for the fact that pulling the image can fail at some point after it has started, in which case the HTTP response has a successful status code but the streaming body ends with an error record. We had to edit the generated code to incorporate any error records in the response body as an error in the overall function result.
And as I wrote in the "sans-io" section of that README, I also believe the generator's approach of providing an API client that has all decisions made for you, like whether the API is sync or async (in languages where that matters), what HTTP library it uses internally, etc is overly limiting. It means that a user who does not agree with even one of those decisions is prevented from using the entire generated library. In my opinion the part where models/requests can be encoded to bytes and models/responses can be decoded from bytes is separate from the part where bytes are sent and received over HTTP, which is why k8s-openapi only does the former and you can write a thin wrapper to use it with your chosen HTTP client.
So basically, the generator exists and is a good starting point when you just want a quick and dirty way to consume an API, but a) you should have a plan for bugs in the spec and bugs in the generated code, b) you should have a plan for generating your own bindings instead of using a third-party library that generated them for you if you don't agree with any of the third party's choice of codegen parameters, and c) you might find hand-written bindings, or a bespoke hand-written generator for that particular spec, worth the investment.
This is where a message centric approach can have advantages. Just generate the objects for the Request and Response DTOs (Data Transfer Objects), and let the developer interact with the API via whatever HTTP/GRPC/MQ client that is appropriate. You can externally provide a client that will use those DTOs directly for a good experience for those that prefer that, but in the end the DTOs are just the messages deserialized into the target language, which is useful in most languages/clients.
Having a complex tool to generate these clients from a complex spec is going to be fraught with issues IMO.
An alternate approach is get the web service API framework to generate just the message contracts in many languages directly. This enables very simple tools to pull down the generated code from a live/running API. We take this approach with the ServiceStack framework [0], and have found it provides a good developer experience. Also supports OpenAPI spec generation.
Each to their own, but I like this approach better than what feels like WSDL client generation all over again.
As we head down our OpenAPI path (https://github.com/fusionauth/fusionauth-openapi has only been built for the last 9 months), I'm very interested in stories like yours. We're very interested in quality SDKs that are easy to update. But since we control the OpenAPI spec for the product, we might have an easier time than you in some ways.
Thanks for sharing!
It's a huge pain to maintain open API files and the generators are of varying quality supporting different specs. They also handle some parts differently in the output.
Which generator have you used for typescript? There are 4 of them and all of them have different issues.
I find the axios one the most stable but keen to know.
How do you also handle refreshing token, incepting request while passing the client through configuration in these?
I find interception and changing response breaks some of the client methods without a way to handle the failure.
Any example or resources you found useful would be appreciated.
The same code you write to validate your request/response should automatically generate the OpenAPI see my comment here:
Or even better the opposite: I have a mini-framework [0] which does the validation and routing based on an OpenAPI spec, so the devs can focus on writing the business logic.
Many legacy codebase don't have an easy way to add schema and make code changes so you end up maintaining a separate file to avoid making changes in the codebase.
Edit: typo.
I now maintain a separate copy of the templates that implemented some missing features of the spec that I needed, and adjusted access control etc to suit my needs. And I also feel like checking the generated code into source control is the way to go, if only for the build time improvements. I mean it doesn't change often enough to warrant being rebuilt all the time.
Ultimately built our own generator because we wanted to ensure specific behaviour for status codes in all of our clients.
We also published the generated clients as NuGet packages for our external consumers, the generated clients had the descriptions etc from the OpenAPI spec embedded in XMLDOC comments (C# code documentation) which helps developers a lot because it appears in IntelliSense in visual studio
I once mad a stab at fixing it, but i got bored after getting feedback from my WIP PR that made it clear no one cared.
It only takes a quick stroll of the issues to find that many features of OpenAPI aren't supported in certain generators, so it's a total waste. I do still like OpenAPI as a design tool and it produces very human readable documentation as well, so it's still useful if you intend to only use it just for that. Just keep your expectations very low when it comes to generation.
Edit: I forgot to mention that clients are subject to breakage if you change the names of models - model names are not actually a part of the contract in my opinion - only the protocol aspects should be (path, params, body, etc.). I don't see how they would solve this but it's a major pain. If you imagine writing a client yourself, it would not break if you tweaked a model name, so this is a huge downside.
I have a scenario where the model names are only there for branding (a numerical enum that has a human readable model name), but are now being hardcoded into the spec in a way I didn't want. I really didn't want to give the models generic, unreadable names in the docs but that seems to be what OpenAPI wants me to do.
For dynamic languages I'd recommend using an http library directly.
Consider the code without a generated client code:
client.get("/hello")
And the code with a generated client
client.hello()
In a dynamic language they're about the same.
But in a statically typed language
- I know the second case is calling a route that exists
- The response is typed
- If there is request data or query parameters the language helps me use them correctly
- My editor displays documentation about the request if I hover over it, including if it has been deprecated
In my latest React Native project I generate the API client and inject functions into React Query.
I know there's Orval that aims to generate React Query right away but I wasn't happy with its developer experience.
We've given up and started to create our own generator. Even this is a hard thing to do as the available parsers in our language are horrible too.
I've found kin-openapi to be very usable for Go:
Amazing how much work and progress was lost when it was all thrown away for REST/JSON.
JSON Schema is great but never reached the level that XSD did.
For the specific API I'm interested in XSD and JSON Schema are mostly equivalent. Quite probably it isn't true in the general case.
Check out some of the test cases in this directory to get an idea how XSD can be translated into JSON Schema https://github.com/ccouzens/vcloud-rest-openapi/blob/main/tr...
In concept, not much.
Maybe slightly easier to implement? More momentum? Certainly a more modern feel (can't think of the last time I saw someone build a new SOAP service, but maybe that's just the circles I run in).
I take it that you’re happy with Orval. What are the biggest downsides?
Another cool tool I found is `rapini` but sadly it has no MSW support.
There's an alternative technology: GraphQL, which has generators too!
Here a PoC of mine demo'ing an architecture in which the backend is very low code (mostly due to Hasura), and most of the app's code is in the frontend: an Elm app with generated type-safe Elm-bindings to the GraphQL backend.
As in the end, you'll spend more time fighting with the tool to generate the code you want than writing it yourself.
That, and bloat.
You compose types representing your request and response shape. Those types validate the API request and it’s response, and also generate the OpenAPI.
Even slight drift between API spec, docs and actual behaviour is impossible in this setup, I couldn’t go back to working any other way!
https://swagger.io/resources/articles/adopting-an-api-first-...
If there are other such projects, please share.
Remember that you don’t have to generate both your server and client APIs from the schema (schema first). Many API servers (I’m particularly aware of OpenAPI and Sanic from Python land) allow you to export the OpenAPI definition from your existing API (code first).
Has anyone had success with generated Kotlin? I’m also curious if there are other Kotlin generators out there.
And since the compiler can't really type check across the API boundary you still need to account for unexpected values. The difference is that the failure happens in ugly auto generated code instead of within your code where it is more easily handled.