I was very excited about this project when I first encountered it as it seemed to be the best centralized community-driven way to generate code for multiple languages and supporting v3.
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?
Fellow "wrote my own generator" person here (I built the codegen system Stripe uses to generate their API client libraries). Sounds like you've got a pretty neat system too, hadn't thought of some of these approaches.
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)
Isn't this the case with all Open Source, that if one really wants to extend something, one must first get into the same mindset as the original designer and understand "their way" of doing it?
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!
Great to see other engineers focusing on this space. I’ve spent a good chunk of my career writing in-house clients for various business critical APIs and have always had a giggle that our self-made ones were often far nicer to use than anything on the market but was always disappointed that due to a variety of corporate constraints they will never see the light of day. I can see a big benefit to bespoke hand-rolled clients with a solid developer experience so keen to see where you guys go!
I tried using it as a Go code generator (I only needed structs and marshaler / unmarshaler helpers), but it ended up being way too difficult to use and I couldn't get it to work properly. After a bit of searching, I bumped into Deepmap's implementation https://github.com/deepmap/oapi-codegen which works great. They even added support for oneOf, anyOf and allOf which makes things a lot easier.
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?
>OpenAPI Generator allows generation of API client libraries from OpenAPI Specs
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.
> It does, but the generated code can be very shitty for some combinations of spec and output language.
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.
I talked to a startup ( https://stainlessapi.com/ ) about a service they provide where they take an OpenAPI spec and build good SDKs on top of it. This included making sure they are idiomatic, included examples, handled exceptions (Edit: like if one SDK consumer needs a slightly different thing because they use a different, custom API, not like network or language exceptions) if needed, and some other goodness. I passed for now because they don't have the language support we need and I am not sure if we need their level of sophistication, but others may benefit from talking to them. (I think the founder helped build Stripe's API docs, IIRC.)
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.
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.
Not that painful when you’ve a dedicated API designer or API writer, or when the teams realize the importance of maintaining OAS files as the single source of truth of API design.
The Swift code that's generated is pretty good, but I found myself pretty much immediately wanting to edit it.
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.
I’ve had similar experiences at my last company with the generated C# code.
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
The python libs are especially worthless. All functions are *kwargs and then the actual arguments are checked with code inside the body of the function.
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.
Have you taken a look at the v6.2.0 and onward 'python' client? It uses type hints, and model new instantiation includes required and optional property signatures. I am the author of that newer python generator.
I also went down the bespoke path after briefly investigating the official Go generator. I ended up forking https://github.com/deepmap/oapi-codegen. The official generator felt very "Java".
Also check out Smithy from AWS (https://github.com/awslabs/smithy), the code it generates is much better. It's influenced by the new AWS SDKs so it has generator support for Rust.
I recommend steering clear of this project entirely - if you really care use something like gRPC instead. Every time I've used it, it has failed spectacularly to generate models as promised and I've had to write the clients by hand anyway. There are just far too many bugs in the respective language generators and it all seems to fall apart when you start using anyOf, oneOf, etc.
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.
The v6.2.0 python client uses type hints everywhere so model property access gives typed results
Endpoint inputs are typed. Endpoint responses are typed so you know what deserialized body models you are getting back. Have you tried it?
I'm not really positive about the community made generator. The spec itself is great, but that's where it stops. Code quality is low of the generated clients and servers, the handlebars templates makes it hard to develop for and don't get me started about the PR reviews. Basically nothing happens.
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.
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).
We used this to generate api functions for our react application and then discovered and switched to Orval. It generates react query functions instead of „normal“ axios functions. Also it has support to generate a complete api mock with msw + faker.js - really nice.
OAPIv3 (fully spec'd/schema'd REST+JSON APIs) is nice, and the generators are important, yet as many point out "not always that great".
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.
Sure, though that goes against the API first design principles that OpenAPI seeks to enable. It also results in poorly documented, often garbled OpenAPI specs. Code-generated specs simply cease to be effective as contracts.
This is something we hope to build relatively soon at Stainless, inspired by Stripe's in-house Ruby API DSL. I think what you're asking for is the right approach.
can only speak for python on the backend, but have been using auto-generated schema from Django, and an auto-generated typescript client for it on the front end for years now.
I’ll soon discover whether doing this is a good idea or not. Will it encourage good practices or hinder them?
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).
I’ve been trying to use this to generate sane, idiomatic Kotlin models, and am so far a little frustrated by the choices the generator makes - especially with regards to how to represent `anyOf` And `allOf` constructs.
Has anyone had success with generated Kotlin? I’m also curious if there are other Kotlin generators out there.
It's been a pain for us too. Personally, I don't really see the point of type checked request/response models. Theoretically, I can see how it's nice but ultimately the source of truth are the domain models on either side. If you do something wrong you'll either see a 400 or be unable to build models from the response.
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.
We ended up building this in-house like most mentioned. Speakeasy and Stainless are productizing it. Our goal was just to make it available for APIs we were offering to others (https://m3o.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é.