tabilzad / ktor-docs-plugin

Provides Ktor Swagger support
34 stars 6 forks source link
android codegen compiler-plugin gradle-plugin kotlin ktor ktor-android ktor-feature ktor-server openapi swagger

Open API (Swagger) Plugin for Ktor

Test and Publish to SonarType

This plugin implements a plug and play solution for generating OpenAPI (Swagger) specification for your Ktor server on any platform with minimal effort - no need to modify your existing code, no special DSL wrappers etc. Just annotate your route(s) definitions with @GenerateOpenApi and openapi.yaml will be generated at build time.

Take a look at the Sample Project to get started.

How to apply the plugin

plugins {
    id("io.github.tabilzad.ktor-docs-plugin-gradle") version "0.6.4-alpha"
}

swagger {

    documentation {
        docsTitle = "Ktor Server Title"
        docsDescription = "Ktor Server Description"
        docsVersion = "1.0"
        generateRequestSchemas = true
        hideTransientFields = true
        hidePrivateAndInternalFields = true
        deriveFieldRequirementFromTypeNullability = true
    }

    pluginOptions {
        format = "yaml" // or json
    }
}

Supported Features

Feature isSupported type
Path/Endpoint definitions Automatic
Ktor Resources Automatic
Request Schemas Automatic
Response Schemas Explicit
Endpoint/Scheme Descriptions Explicit
Endpoint Tagging Explicit

Plugin Configuration

Documentation options

Option Default Value Explanation
docsTitle "Open API Specification" Title for the API specification that is generated
docsDescription "Generated using Ktor Docs Plugin" A brief description for the generated API specification
docsVersion "1.0.0" Specifies the version for the generated API specification
generateRequestSchemas true Determines if request body schemas should
be automatically resolved and included
hideTransientFields true Controls whether fields marked with @Transient
are omitted in schema outputs
hidePrivateAndInternalFields true Opts to exclude fields with private or internal modifiers from schema outputs
deriveFieldRequirementFromTypeNullability true Automatically derive object fields' requirement from its type nullability
servers empty List of server URLs to be included in the spec (ex: listOf("http://localhost:8080")

Plugin options

Option Default Value Explanation
enabled true Enable/Disables the plugin
saveInBuild true Decides if the generated specification file should
be saved in the build/ directory
format yaml The chosen format for the OpenAPI specification
(options: json/yaml)
filePath $modulePath/build/resources/main/openapi/ The designated absolute path for saving
the generated specification file

How to use the plugin

Generating endpoint specifications

Annotate the specific route definitions you want the OpenAPI specification to be generated for.


@GenerateOpenApi
fun Route.ordersRouting() {
    route("/v1") {
        post("/order1") {
            /*...*/
        }
    }
}

You could also annotate the entire Application module with multiple/nested route definitions. The plugin will recursively visit each Route. extension and generate its documentation.


@GenerateOpenApi
fun Application.ordersModule() {
    routing {
        routeOne()
        routeTwo()
    }
}

fun Route.routeOne() {
    route("/v1") { /*...*/ }
}

fun Route.routeTwo() {
    route("/v2") { /*...*/ }
    routeThree()
}

Endpoint and field descriptions

Describe endpoints or schema fields.

data class RequestSample(
    @KtorFieldDescription("this is a string")
    val string: String,
    val int: Int,
    val double: Double
)

@GenerateOpenApi
fun Route.ordersRouting() {
    route("/v1") {
        @KtorDescription(
            summary = "Create Order",
            description = "This endpoint will create an order",
        )
        post("/create") {
            call.receive<RequestSample>()
        }

        route("/orders") {
            @KtorDescription(
                summary = "All Orders",
                description = "This endpoint will return a list of all orders"
            )
            get { /*...*/ }
        }
    }
}

Responses

Defining response schemas and their corresponding HTTP status codes are done via @KtorResponds annotation on an endpoint.

@GenerateOpenApi
fun Route.ordersRouting() {
    route("/v1") {
        @KtorResponds(
               [
                   ResponseEntry("200", Order::class, description = "Created order"),
                   ResponseEntry("400", ErrorResponseSample::class, description = "Invalid order payload")
               ]
        )
        post("/create") { /*...*/ }
        @KtorResponds([ResponseEntry("200", Order::class, isCollection=true, description = "All orders")])
        get("/orders") { /*...*/ }
    }
}

Tagging

Using tags enables the categorization of individual endpoints into designated groups. Tags specified at the parent route will propogate down to all endpoints contained within it.

@Tag(["Orders"])
fun Route.ordersRouting() {
    route("/v1") {
        post("/create") { /*...*/ }
        get("/orders") { /*...*/ }
    }
    route("/v2") {
        post("/create") { /*...*/ }
        get("/orders") { /*...*/ }
    }
}

On the other hand, if the tags are specified with @KtorDescription or @Tag annotation on an endpoint, they are associated exclusively with that particular endpoint.

@GenerateOpenApi
fun Route.ordersRouting() {
    route("/v1") {
        @KtorDescription(tags = ["Order Operations"])
        post("/order") { /*...*/ }
        @Tag(["Cart Operations"])
        get("/cart") { /*...*/ }
    }
}

Planned Features

Sample Specification

sample