Baeldung Pro – Kotlin – NPI EA (cat = Baeldung on Kotlin)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

1. Overview

API documentation is a crucial aspect of any web service, providing a clear and structured way for developers to understand and interact with any API.

OpenAPI (formerly known as Swagger) is a widely adopted standard for creating comprehensive API documentation.

In this article, we’ll walk through the steps to set up a Kotlin project using Ktor and integrate OpenAPI to generate and display API documentation.

2. Project Set Up

Before starting with the implementation, we’ll need to set up our Ktor project’s structure and add the required dependencies.

2.1. Create a New Kotlin Project

The first step is to create a new Kotlin project in any IDE, such as IntelliJ IDEA. We’ll have to select Kotlin and Ktor as our project type.

2.2. Add Necessary Dependencies

To support OpenAPI, we need to include the required plugins and dependencies for Ktor and OpenAPI. We’ll also have to include the swagger-codegen-generators dependency that will be in charge of generating the documentation site:

plugins {
    id("io.ktor.plugin") version "2.3.11"
    ...
}

dependencies {
    implementation("io.ktor:ktor-server-core:$ktor_version")
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("io.ktor:ktor-server-swagger:$ktor_version")
    implementation("io.ktor:ktor-server-openapi:$ktor_version")
    implementation("io.ktor:ktor-server-cors:$ktor_version")
    implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
    implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
    implementation("io.swagger.codegen.v3:swagger-codegen-generators:$swagger_codegen_version")
    testImplementation("io.ktor:ktor-server-test-host:$ktor_version")
    ...
}

3. Documentation File

Now that the project is properly set up, we can start with the implementation by creating the documentation file where we can define the details of each endpoint of the API.

3.1. Create the Documentation File

OpenAPI allows us to define the description of each service available in our API using a YAML file descriptor.

We’ll take as a case study an API to create and read Products of any generic store:

openapi: "3.0.3"
info:
  title: "Products API"
  description: "API to create and retrieve Products"
  version: "1.0.0"
servers:
  - url: "http://0.0.0.0:8080"
paths:
  /product:
    post:
      description: "Creates a new product"
      requestBody:
        description: "A JSON object that represents a product"
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Product"
      responses:
        "201":
          description: "Created"
          content:
            text/plain:
              schema:
                type: "string"
              examples:
                Example#1:
                  value: "Product saved successfully"
  /product/{id}:
    get:
      description: "Returns a product by the ID"
      parameters:
        - name: "id"
          in: "path"
          required: true
          schema:
            type: "string"
      responses:
        "200":
          description: "OK"
          content:
            '*/*':
              schema:
                $ref: "#/components/schemas/Product"
components:
  schemas:
    Product:
      type: "object"
      properties:
        id:
          type: "integer"
          format: "int32"
        name:
          type: "string"
        price:
          type: "number"
          format: "double"

Working with OpenAPI and Ktor also allows the generation of the YAML documentation dynamically from the code using annotations to specify the endpoint information.

4. Structure of the Product API Application

4.1. Create the Application File

Let’s create the main application file that will serve as the entry point for our Ktor application. This file will set up the basic structure, including defining the Product data class and initializing the server:

@Serializable
data class Product(val id: Int, val name: String, val price: Double)

fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080, module = Application::main).start(wait = true)
}

fun Application.main() {
    val productStorage = mutableListOf<Product>()
    productStorage.addAll(
        arrayOf(
            Product(1, "Laptop", 999.99),
            Product(2, "Smartphone", 499.99)
        )
    )

    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
        })
    }
    install(CORS) {
        anyHost()
        allowHeader(HttpHeaders.ContentType)
    }
}

4.2. Enabling Swagger UI

To make the API documentation accessible and user-friendly, we’ll enable Swagger UI. This tool provides a graphical interface for interacting with the API endpoints:

fun Application.main() {

    routing {
        // API endpoints

        swaggerUI(path = "swagger", swaggerFile = "openapi/documentation.yaml") {
            version = "4.15.5"
        }
    }
}

This configuration will make Swagger UI available at the /swagger path, using the specified OpenAPI documentation.

4.3. Product API

Finally, we’ll implement the actual API endpoints as per the product API documentation. These endpoints allow users to retrieve and create products:

fun Application.main() {
    // Swagger UI setup

    routing {
        get("/product/{id}") {
            val id = call.parameters["id"]
            val product: Product = productStorage.find { it.id == id!!.toInt() }!!
            call.respond(product)
        }

        post("/product") {
            val product = call.receive<Product>()
            productStorage.add(product)
            call.respondText("Product saved successfully", status = HttpStatusCode.Created)
        }

        openAPI(path="openapi", swaggerFile = "openapi/documentation.yaml") {
            codegen = StaticHtmlCodegen()
        }
    }
}

5. Running and Testing the API

With the configurations in place, we can run the application by executing the main function in Application.kt. In IntelliJ IDEA, we can do this by clicking the run icon next to the main function or by using the terminal with the command ./gradlew run.

5.1. SwaggerUI

As we have defined the proper route to SwaggerUI, we can now navigate to http://localhost:8080/swagger to see the available endpoints. For example, we can create a new product by making a POST request to the /product endpoint as follows:

Create product - POST request

 

Now, to check if the product was created correctly, we can use a GET request to the /product endpoint:

Get product request

 

We should see the response from the server with the data of the product we have created before:

Get product response

 

 

 

5.2. Openapi Documentation

Finally, we can navigate to the following URL: http://localhost:8080/openapi, where the complete API documentation has been generated:

OpenAPI Documentation

 

From this documentation, we can check implementation examples for a variety of SDKs.

6. Conclusion

In this article, we’ve demonstrated how to set up a Kotlin project using Ktor and integrate OpenAPI (Swagger) to generate and display API documentation. This setup provides a clear and interactive way for developers to understand and interact with any API.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.