Search code examples
springswaggeropenapispringdoc

Swagger UI with springdoc for specification-first approach


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'

Solution

  • 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.