Create your REST API using OpenAPI kotlin-spring generator
Tags: kotlin, springboot, openapi, maven
Published: 2021-11-11 12:04:09

When building an API, I like to start by first creating a formal definition before starting the actual development. One of the most established standards for such API definitions is OpenAPI (formerly Swagger). One pleasant aspect of starting with a definition is that, once the definition is created and agreed upon, we can generate part of our codebase using the OpenAPI Generator. As the name suggests, the OpenAPI Generator generates code from an OpenAPI specification. More specifically, the generator is able to create code for client libraries, server stubs, documentation, and configuration in many different languages and frameworks.

I know this generator from my Java projects and decided to use it with Kotlin as well. Lucky for me, there is actually a generator for Spring Boot-based Kotlin projects! I, however, could not find a tutorial dedicated to it and therefore I share my steps here in this tutorial.

# TL;DR
You can find the full project (including `target`) at [https://github.com/BjornvdLaan/kotlin-spring-boot-open-api-example](https://github.com/BjornvdLaan/kotlin-spring-boot-open-api-example).

# What we want to achieve
We wish to generate the server stub using the OpenAPI Generator via its Maven plugin. An advantage of using the Maven (or Gradle) plugin is that the generation becomes part of our API’s build lifecycle and the generated code is always in sync with your OpenAPI definition when you clean compile your code.

For our example in this tutorial, we will use the [PetStore OpenAPI specification]() from [this tutorial]() on Baeldung. After running the Maven plugin, the generated code is found in the `target/generated-sources/openapi` directory (see below). After generation, we ‘only’ still need to implement the generated Delegate interfaces within our own `src` directory and, voila, the API is implemented. We will implement one of the routes in the PetStore specification and test it using curl.

![Generated sources directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2ggryipqdjmjpkdhwpi.png)
 
# Create your Spring Boot project
We start by creating a fresh Spring Boot project using the [Spring Initializr](https://start.spring.io/). We tell it to be a Maven project using Kotlin and packaged as a jar. We also add the Spring Web dependency because the generated code depends on this dependency.

![Spring Initializr](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dwmw5n0dvdt05bg91toy.png)

After downloading the project, we open it in our favorite IDE and we add the `petstore.yml` OpenAPI definition to `src/main/resources` to end up with the following project structure.

![Project structure before generation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tm6ld71tq4281pqcavj.png)

Before we can start, we need to add one more dependency to our `pom.xml` that the generated code depends on:

```xml
<dependency>			 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
```

# Add the Maven plugin
Now it's time to add the OpenAPI Generator's Maven plugin to `pom.xml`. 

```html
<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>5.1.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>
                    ${project.basedir}/src/main/resources/petstore.yml
                </inputSpec>
                <generatorName>kotlin-spring</generatorName>
                <configOptions>
                    <basePackage>nl.bjornvanderlaan.petstoreapi</basePackage>
                    <apiPackage>nl.bjornvanderlaan.petstoreapi.api</apiPackage>
                    <modelPackage>nl.bjornvanderlaan.petstoreapi.model</modelPackage>
                    <configPackage>nl.bjornvanderlaan.petstoreapi.config</configPackage>
                    <delegatePattern>true</delegatePattern>
                    <interfaceOnly>false</interfaceOnly>
                    <modelNameSuffix>Dto</modelNameSuffix>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>
```
 
An overview of all configuration options is found [here](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/kotlin-spring.md). I would like to highlight three options where I made a design decision:

1. **[delegatePattern = true]** - I asked the generator to use the Delegate pattern so the routes and their logic are separated. The Delegate implementations that we write ourselves can be injected in the generated code, without any need to manually touch the generated sources.
2. **[interfaceOnly = false]** - This option determines whether the controller implementation will also be generated. This controller implementation essentially just autowires the relevant Delegate to be used in the routes. I feel its good to let it be generated because then no engineer can be tempted to add other functionality to the controller, maintaining better separation of concerns. If you really want to do a lot more in your controller then it is worth considering to set this option to **true**.
3. **[modelNameSuffix = Dto]** - I prefer to use the Dto (Data Transfer Object) pattern to separate these objects from the entities that I will use in the service layer. As such, I add a suffix to make the pattern usage explicit.

# Generate the code
We can generate the code by running `mvn clean generate-sources` and we then obtain the following project structure:

![Project structure after generation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2uk4o1hvm3f22qn3jrb.png)

*For IntelliJ users: the `target/generated-sources/openapi/src/main/kotlin` folder can be marked as an generated sources root. Right-click this folder, then go to 'Mark Directory as' and select Generated Sources Root.*

The `target/generated-sources/openapi/src/main/kotlin` now contains two packages with a couple of classes each. Let's quickly go through them one by one.

## api.PetsApi.kt
This interface class defines one method for each API route from our specification using Spring Web annotations like `@RequestMapping`, `@GetMapping`, and `@PostMapping`.
The methods are all one-liners as they simply delegate the work to the Delegate, exactly as we configured.
The interface also has an unimplemented `getDelegate()` which will be implemented by `PetsApiController`.

## api.PetsApiDelegate
This interface defines the methods used by the `PetsApi` with a standard implementation that just returns status 501 Not Implemented. We will implement the interface in our `src` folder.

## api.PetsApiController.kt
The Controller implements the PetsApi interface by autowiring the PetsApiDelegate implementation. As mentioned, I configured OpenAPI Generator to generate this class for me because I feel this is all the Controller should do.

## api.Exceptions
This `@ControllerAdvice` class defines default exception handling for three exceptions that are commonly encountered.

## api.ApiUtils
This object (singleton) defines a utility method used by a utility method for GET mappings.

## model.ErrorDto
This Dto can be used to send back erroneous response to the requestor.

# model.PetDto
The Dto that resembles our pets and is used to respond to, for instance, GET requests that want to retrieve data. Our generated code contains two examples of such routes: `listPets` and `showPetById`.

# Implement the Delegate
Now it's finally time to start coding. We create a new class in `nl.bjornvanderlaan.petstoreapi.api` package (in `src`, not `target`!!) and we override the `listPets` method to let it return an hardcoded response with my three favorite cat names and status 200 (Ok).

```kotlin
package nl.bjornvanderlaan.petstoreapi.api

import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.stereotype.Component
import nl.bjornvanderlaan.petstoreapi.model.PetDto

@Component
class PetsApiDelegateImpl : PetsApiDelegate {
    override fun listPets(limit: Int?): ResponseEntity<List<PetDto>> {
        return ResponseEntity(
            listOf(
                PetDto(1L, "Charlie"),
                PetDto(2L, "Obi"),
                PetDto(3L, "Loki"),
            ),
            HttpStatus.OK
        )
    }
}
```

# Verify the API
Great, so we have some generated classes in our `target` directory based on our OpenAPI specification and we also added some code of our own in `src`. Let's run this with IntelliJ and see what happens.

![Spring Boot application is running on port 8080](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0q7lfoybp3vns59n907q.png)

We learn that the Spring Boot application is now running on port 8080. But does it actually work?

```bash
$ curl -I http://localhost:8080/pets
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: <redacted my author>

$ curl http://localhost:8080/pets
[{"id":1,"name":"Charlie","tag":null},{"id":2,"name":"Obi","tag":null},{"id":3,"name":"Loki","tag":null}]
```

# Compile your code
We might suspect that this tutorial is finished as we have verified the API's response. However, when we try to compile our code using `mvn clean compile` we are met with some red text: compilation fails because the generated classes cannot be resolved. 

Why does this happen? The problem is that the `kotlin-maven-plugin` does not see `target/generated-sources/openapi` as a source directory and so these files are not compiled after cleaning. We resolve this by explicitly adding the path to the plugin's configuration:

```xml
<plugin>
	<groupId>org.jetbrains.kotlin</groupId>
	<artifactId>kotlin-maven-plugin</artifactId>
	<configuration>
		<!-- omitted by author -->
	<executions>
		<execution>
			<id>compile</id>
			<goals>
				<goal>compile</goal>
			</goals>
			<configuration>
				<sourceDirs>
					<sourceDir>${project.build.directory}/generated-sources/kotlin/src/main/kotlin</sourceDir>
				</sourceDirs>
			</configuration>
		</execution>
	</executions>
	<dependencies>
	    <!-- omitted by author -->
	</dependencies>
</plugin>
```

Adding the above execution resolves our issue and Maven now tells us the build is successful.

![Maven compile successful](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yffbeoyxab0ustz5sjk6.png)
 
Great! Now we are done, right? Not exactly. When we go a step further and run our Spring Boot application via `mvn spring-boot:run` then we are met with another error:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ntczw2wc8caft5m3eb8.png)

So now what? The issues is caused by our design choice to generate not only the interfaces but also the Controller classes as the generator then also gives us an `Application.kt` class. As we already also have a main class in our `src` project, Spring now cannot determine which to pick. There are essentially two ways to fix this. The first option is to delete our own `PetStoreApplication.kt` and use the generated class. This could be a reasonable choice if you are use that you do not want to do anything custom in the main class. Alternatively, we can configure `spring-boot-maven-plugin` to point out the correct main class:

```xml
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <mainClass>nl.bjornvanderlaan.petstoreapi.PetstoreApiApplicationKt</mainClass>
        </configuration>
    </plugin>
</plugins>
```

Regardless of which option you choose, we can run `mvn spring-boot:run` again and are then at last greeted with our beloved Spring banner and the message that our application can now be found at port 8080!

# Conclusion
OpenApi allows us to create a specification for our API that we can share among teams and stakeholders to communicate our contracts. The OpenAPI Generator and its Maven plugin help us to generate code from our specification. We ourselves can then focus on the business logic. In this tutorial, we saw how we can generate a server stud from our specification for Spring Boot-based Kotlin projects.