>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".
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.