As part of my series “An opinionated Kotlin backend service”, I was researching ways to generate OpenAPI documentation automatically (the problem). Before explaining how to generate api documentation from code (the solution), I will give a short overview of different approaches to generating such documentation.
OpenAPI is the standard to describe APIs since the developers of RAML joined the OpenAPI Initiative (it’s a bit more complicated but this isn’t an article about the different standards…). If you want your backend service to be consumed properly it should have an OpenAPI documentation.
I already have an API
If you already have an API and only need the documentation, use one of the various Swagger tools (like Swagger Inspector, Swagger Editor or the other Swagger Editor) to document your API and then publish it as part of your app or host it with e.g. SwaggerHub. BTW I’m not paid by Swagger and I’m aware, that there are alternatives to Swagger ;-).
I don’t have an API yet
If you don’t have an API or want to transition your existing API towards auto-generated documentation, there are three approaches imo:
- Generate the routes / server stubs from the API documentation:
API documentation ➔ Code generation
- Generate the API documentation from your code documentation:
Code documentation ➔ API documentation
- Generate the API documentation from the routing code:
Routing code ➔ API documentation
API documentation ➔ Code Generation
In this case the API documentation is written and a code generator generates the server and/or client stubs.
The most popular and complete generator is probably the https://github.com/OpenAPITools/openapi-generator. It supports many different target platforms, kotlin-server being one of them. If you want to check out generated code I’d recommend to try Swagger Codegen first. It’s easier to use than the openapi-generator and has a kotlin-server option too:
Code documentation ➔ API documentation
In this case code is documented by using annotations and/or a documentation generator like JavaDoc, JSDoc or YARD. Extensions can be used to create OpenAPI documentation on top of the regular code documentation.
E.g. for nodejs there is swagger-jsdoc generating OpenAPI documentation from annotations and JSDoc:
For Java there is Swagger Core:
Routing Code ➔ API documentation
In this case the API is documented as part of the code and a generator creates the OpenAPI documentation by parsing the code (plugin e.g.) or by extending routing libraries to add the documentation features.
This option is different from the “Code documentation ➔ API documentation” option because it’s using the same language for the actual route and for the API documentation (compared to annotations, configuration files or specific documentation “languages”).
For Ruby on Rails there’s e.g. rswag:
For Ktor/Kotlin there’s Ktor-OpenAPI-Generator:
There are two decisions:
- Generating code or documentation?
- Generating documentation from code or from annotations/specific documentation?
The first decision was an easy one for me once I checked out the generated code. Unlike for Java, the kotlin-server code generated by https://github.com/OpenAPITools/openapi-generator is quite rudimentary and adds little value because each code generation requires manual integration. There also doesn’t seem to be much interest in using it for Ktor (there’s only one single question on SO related to openapi-generator and ktor). Code generation would be useful if the actual business logic can be plugged in easily and no manual changes are needed after a code generation run.
The second decision was easy for me too. As already explained in https://medium.com/nerd-for-tech/object-validation-in-kotlin-c7e02b5dabc I’m advocating for using a single language instead of having separate languages for specific purposes (configuration, documentation, routing definition etc.) so I decided to dig deeper and use Ktor-OpenAPI-Generator for my purpose.
The project https://github.com/papsign/Ktor-OpenAPI-Generator is not very active, 8 watchers, 122 stars and 25 forks. Last commit March 17th 2021 and it’s at version 0.2-beta.16.
The documentation is very lacking, the examples here https://github.com/papsign/Ktor-OpenAPI-Generator/wiki/A-few-examples are more confusing than enlightening. The demo project https://github.com/SerVB/e-shop is rather old. It does explain more than the “few examples” but as a quick start guide it’s certainly not ideal.
The one thing I tripped over right away and which almost made me give up on the library was the so-called DSL. It’s a mixture between nested object generation, constructor object instantiation, extension functions and actual DSL.
Setting up the plugin looks still promising (actual DSL):
Setting up routes is already awkward:
Adding exception handling makes it even worse (this is an actual example from https://github.com/SerVB/e-shop):
While I can see the benefit of hierarchical routes, I don’t understand why the throws statements wrap around the routes and why they need to wrap each other (as a matter of fact they don’t have to but the lack of documentation makes this hard to figure out).
The generated documentation looks decent:
It’s open source!
That said I forked the project and started eliminating some of the shortcomings: https://github.com/1gravity/Ktor-OpenAPI-Generator.
First I changed the exception handling to flatten the hierarchy. Instead of having multiple extension functions with different signatures, the APIExceptionBuilder can be used to build an exception (properties are optional using the builder pattern):
Second I added the exception as parameter to the route functions so instead of wrapping a route into a throws call, an exception definition can be passed into a route definition:
I plan to eliminate the constructor invocation and replace it with a DSL using a route builder to get to a structure like this:
Another idea is to scrap this new DSL completely and write extension functions or wrappers around the regular Ktor routing to use it as a drop-in to make adoption easy for Ktor developers.
Please join me at https://github.com/1gravity/Ktor-OpenAPI-Generator to make this happen!
As usual please don’t hesitate to provide feedback and happy coding!