4

Background: In our (Kotlin) Springboot 3 project, we used the specification-first approach, and generated code from our openapi.yml specification using openapi-generator.

Now we want to publish the api docs, and display a Swagger UI page, with URLs following the usual conventions. When following the advice at Question 12.66 in the Springdoc FAQ, we end up with the yml file served as static content.

How can I achieve that the usual conventions are followed, i.e. the json spec is available at http://localhost:8080/my-app/api/api-docs, and the swagger UI at http://localhost:8080/my-app/api/swagger-ui.html?

UPDATE: As additional background, here is the snippet from build.gradle.kts that generates the code for the API:

openApiGenerate {
  inputSpec.set("$rootDir/src/main/resources/static/my-app/api/openapi.yml")
  outputDir.set(outputDirPath.get().toString())
  packageName.set(apiPackageName)
  apiPackage.set("$apiPackageName.api")
  modelPackage.set("$apiPackageName.model")
  modelNameSuffix.set("Dto")
  generatorName.set("kotlin-spring")
  configOptions.set(
      mapOf(
          "useSpringBoot3" to "true",
          "delegatePattern" to "true",
          "interfaceOnly" to "false",
          "dateLibrary" to "java8",
          "useTags" to "true",
          "enumPropertyNaming" to "UPPERCASE"
      )
  )
}

The generated code that corresponds to the openapi.yml looks correct. The code generation is executed before the build, and everything is in a single project.

tasks.withType<KotlinCompile> {
    dependsOn(tasks.openApiGenerate)
    mustRunAfter(tasks.openApiGenerate)
    kotlinOptions {
        freeCompilerArgs = listOf("-Xjsr305=strict")
        jvmTarget = "17"
    }
}

Snippet with the used dependencies:

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-mustache")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.springdoc:springdoc-openapi-data-rest:1.6.15")
    implementation("org.springdoc:springdoc-openapi-ui:1.6.15")
    implementation("org.springdoc:springdoc-openapi-kotlin:1.6.15")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    /* further deps related to database and test */
}

Snippet from application.yml:

springdoc:
  api-docs:
    path: /api-docs
    groups:
      enabled: true
  swagger-ui:
    url: /my-app/api/openapi.yml
server:
  servlet:
    context-path: '/my-app/api'
Hermann.Gruber
  • 1,257
  • 1
  • 12
  • 37
  • Didn't get your requirement. I am expecting you are using the "org.springdoc" dependency in your spring boot project. By default if we don't specify any custom path in the application.properties then JSON api-doc will be available at http://localhost.com/APP-CONTEXT-PATH-IF-DEFINED/v3/api-docs and YAML api-doc will be available at http://localhost.com/APP-CONTEXT-PATH-IF-DEFINED/v3/api-docs.yaml. These docs will be generated by spring-boot based on the APIs present. – Harsh Kanakhara May 04 '23 at 07:39
  • @HarshKanakhara thank you for caring about this. I think I have a similar problem as here: https://stackoverflow.com/questions/74701738/spring-boot-3-springdoc-openapi-ui-doesnt-work springdoc seems not to generate the respective URLs in my project. I want to know what is needed in build.gradle.kts and application.yml to make this work. – Hermann.Gruber May 04 '23 at 13:30
  • Would you mind sharing your build.gradle.kts generateTask configuration? I suspect you are using the `interfaceOnly` setting set to true. However, it could be any number of issues that could more easily be evaluated by providing the gradle task, your current project structure (i.e. is it a multi project build, are you importing your generated code from another repo, or are you building your generated code and production code in the same project), and your current `application.yml` configuration. – tbatch May 04 '23 at 18:02
  • @tbatch thank you for your suggestion. I updated the question accordingly. – Hermann.Gruber May 08 '23 at 08:32

1 Answers1

1

Using an openapi.yml specification, you have embraced a specification-first approach for your Kotlin Springboot project. The openapi-generator has been utilized by you to produce code from this specification. Your aim now is to display a Swagger UI page that follows the customary conventions and release the API documentation.

The Swagger UI ought to be located at http://localhost:8080/my-app/api/swagger-ui.html, with the JSON spec available at http://localhost:8080/my-app/api/api-docs for optimal attainment.

Consider the relevant configuration first. In the build.gradle.kts file, the code generating the API is created with the following snippet.

openApiGenerate {
  inputSpec.set("$rootDir/src/main/resources/static/my-app/api/openapi.yml")
  outputDir.set(outputDirPath.get().toString())
  packageName.set(apiPackageName)
  apiPackage.set("$apiPackageName.api")
  modelPackage.set("$apiPackageName.model")
  modelNameSuffix.set("Dto")
  generatorName.set("kotlin-spring")
  configOptions.set(
      mapOf(
          "useSpringBoot3" to "true",
          "delegatePattern" to "true",
          "interfaceOnly" to "false",
          "dateLibrary" to "java8",
          "useTags" to "true",
          "enumPropertyNaming" to "UPPERCASE"
      )
  )
}

The code will be generated from the openapi.yml, and based on this particular setup it looks to be accurate. All components are contained within one project, and the code generation is completed prior to the build.

Included in your build.gradle.kts is a selection of dependencies that are being utilized.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-mustache")
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.springdoc:springdoc-openapi-data-rest:1.6.15")
    implementation("org.springdoc:springdoc-openapi-ui:1.6.15")
    implementation("org.springdoc:springdoc-openapi-kotlin:1.6.15")
    implementation("org.springframework.boot:spring-boot-starter-validation")
    developmentOnly("org.springframework.boot:spring-boot-devtools")
    /* other dependencies related to database and testing */
}

The relevant snippet can be found in the application.yml file, which we'll dive into next.

springdoc:
  api-docs:
    path: /api-docs
    groups:
      enabled: true
  swagger-ui:
    url: /my-app/api/openapi.yml
server:
  servlet:
    context-path: '/my-app/api'

"/api-docs" is the path specification for the API documentation in the present layout, while the Swagger UI URL is determined by the "swagger-ui" section, which in this instance, is set to "/my-app/api/openapi.yml".

At http://localhost:8080/my-app/api/api-docs, you can find the JSON spec that should be followed, while the Swagger UI is accessible at http://localhost:8080/my-app/api/swagger-ui.html. To comply with the necessary conventions, here are some adjustments that you may need to make:

1.Your application.yml file needs to be updated by following these steps in the "springdoc" section:

springdoc:
  api-docs:
    path: /my-app/api/api-docs
    groups:
      enabled: true
  swagger-ui:
    url: /my-app/api/api-docs

2.In your build.gradle.kts file, make changes to the "openApiGenerate" configuration. To personalize the path, modify the "outputDir" with a convention of your liking. See an example below:

outputDir.set("$rootDir/src/main/resources/static/my-app/api")

For serving as static content, the openapi.json file that is generated will be placed in the correct directory to ensure it is in the appropriate location.

Using the preferred URLs, accessing Swagger UI and API documentation should now be uncomplicated with these recent modifications.

The specification for JSON can be found at http://localhost:8080/my-app/api/api-docs. My-App API now comes equipped with Swagger UI, which can be accessed at http://localhost:8080/my-app/api/swagger-ui.html.

To activate these changes, it may be necessary for you to rebuild and redeploy your application.

Qasim Parry
  • 192
  • 1
  • I noticed that with trace logging activated for web, the swagger-related handler mappings are logged only after I replace the three springdoc-related deps as described in my question with the following dependency: implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0") – Hermann.Gruber May 10 '23 at 13:56
  • Minor catch: in the springdoc/api-docs section, we need `path: /api-docs`. Otherwise the json spec will be served at `localhost:8080/my-app/api/my-app/api/api-docs/`. At least this applies if we use the springdoc-openapi-starter-mvc-ui dependency. – Hermann.Gruber May 10 '23 at 14:03