Customize OpenAPI Generator output for your Android App

Android

Software Architecture

Getting started

What is OpenAPI (and Swagger)?

OpenAPI is a way to describe web services API. Basically, it allows to define API routes (with path, allowed parameters, HTTP verb...) and models (object models corresponding to API response or body and its components). All this information is contained in an OpenAPI specification written in YAML or JSON.

You may have heard about Swagger and its visible part Swagger UI. OpenAPI is simply the new name of Swagger after it has been transferred to the open-source community.

What is OpenAPI Generator?

Since the OpenAPI specification contains everything needed to interact with the API, we can generate the code required to interact with the API based on this file. And since REST API often expose a large data model with dozens of entities, it's very tenting to generate them. For exemple, the Twitter API used in this article is composed of more than 180 entities that you would have to write by hand.

Fortunately, OpenAPI Generator can generate everything from the model classes to an entire Gradle module for your data layer!

Install OpenAPI Generator

Open API Generator can be integrated using a Gradle plugin, but during development, I recommend using the CLI client which is more convenient during the configuration of the project.

Get the OpenAPI specification for your web service

This is the part where there is no magic. If the backend is developed by a separate team, they should already give you a corresponding OpenAPI or Swagger specification as part of the webservice documentation.

The goal of this article is not to cover how to write an OpenAPI specification, so let's take an existing one!

Basic generation

As an example, we can get a public API specification from Twitter.

In a new directory, download the specification file:

$ wget https://api.twitter.com/2/openapi.json

Then, let’s generate some code based on this:

$ openapi-generator generate -g kotlin -i openapi.json

The command generate indicates that you want to generate something based on the -i openapi.json file. The -g option indicates the generator you want to use.

Dozens of languages and platforms are supported such as swift5, typescript, python... You can see all supported generators with $ openapi-generator list.

Run the command, and boom: An entire Kotlin SDK for Twitter has been generated!

1-generated-models-in-intellij

what’s available?

There is now a complete standalone Gradle module with:

  • Gradle build files

API documentation (Markdown) in docs2-generated-documentation-in-markdown



  • API models in src/.../models

    • Contains all model classes specified by the specification, and used by the endpoints. By default, Moshi JSON parser is used for serialization.3-generated-model-in-kotlin


     
  • API methods in src/.../apis

    • Contains a method for every endpoint specified, using OkHttp by default as a client.4-generated-method-in-kotlin

    And since comments are generated from the specification file, it’s probably more commented than hand-written code!

Customization

Generate only some parts of the module

Generating the whole module is a good starting point to see what’s available, but it may be difficult to integrate into your current architecture. For instance, you may already have a configured OkHttp instance to use, and want to use Retrofit. At the end, we may be only interested in generating the model classes and the API classes.

Use the --global-property models --global-property apis options to generate only these classes.

As an example, wouldn’t it be nice to put these classes directly into your app module? Let’s add more options:

-o app will target this directory for the generated code

--model-package "com.myapp.mydatalayer.entities" Allows control in which package the model classes are generated

--api-package "com.myapp.mydatalayer.api" will do the same for the API classes.

Customize your models

At this point we may not want to use Moshi as a parser. Want to use the fancy kotlinx.serialization? Add this option:

--additional-properties=serializationLibrary=kotlinx_serialization

gson and jackson are also supported.

It is a good practice to identify at a glance if a model class is used in your data layer or your domain layer. Let’s add an Entity suffix to allow that!

--model-name-suffix "Entity"

Now, all model classes end with this.

5-entity-suffix-in-model-file-names

Customize API interface files

Now, we may want to use our good old friend Retrofit for the API.

Let’s add this to --additional-properties=library=jvm-retrofit2,...

To use Kotlin coroutines for the Retrofit interface: --additional-properties=useCoroutines=true,...

Note that additional-properties should be concatenated using ,our final option looks like: --additional-properties=library=useCoroutines=true,jvm-retrofit2,serializationLibrary=kotlinx_serialization

Use custom templates

If you tried to compile the module at the previous steps, you may have found that it, unfortunately, doesn’t compile 😭. As meta-programming is very powerful, it’s also a common pitfall you may encounter, so let’s try to fix this!

OpenAPI-generator works with templates written with the mustache template language. To access the templates used previously, use the author template command.

$ openapi-generator author template -g kotlin -o template

You can then browse the template directory. Some interesting files:

  • data_class.mustache, data_class_opt_var.mustache and data_class_req_var.mustache are used to generate the models.

  • libraries/jvm-retrofit2/api.mustache is used to generate the Retrofit API interfaces that we configured previously.

  • licenseInfo.mustache is the header for the model classes.

The purpose of this is not to explain the mustache template language, but some fixes on the templates were necessary.

build.gradle integration

Finally, we want to be able to generate the file on demand without having to install the CLI client.

The OpenAPI Generator Gradle plugin make it possible!

After having setup the plugin,

Let’s convert the command line we’ve been methodically building to a set of option:

openApiGenerate {
globalProperties = [
supportingFiles: "",
apis : "",
models : "",
]
inputSpec = "openapi.json"
generatorName = "kotlin"
outputDir = "$rootDir/app"
templateDir = "template"
modelPackage = "fr.haan.openapi_playground_app.data.entities"
apiPackage = "fr.haan.openapi_playground_app.data.api"
modelNameSuffix = "Entity"
additionalProperties = [
useCoroutines : "true",
library : "jvm-retrofit2",
serializationLibrary: "kotlinx_serialization",
sourceFolder : "src/main/java",
]
}

We can now generate using gradle CLI: $ ./gradlew openApiGenerate

That’s it! Our app is now ready to follow the backend REST API developments without having to manually write entities or interfaces!

Final thoughts

As you can see, a lot of power comes from the spec being perfectly written (with strong types, accurate documentation, etc...) I guess the best way to achieve this is to also generate the server side using the same specification.

Anyway, I hope you enjoyed reading this! You will find the complete step-by-step repository with all the example code of this article here.

Articles similaires