Notarized Resources

The Ktor Resources API allows users to define their routes in a type-safe manner.

You can read more about it here.

Kompendium supports Ktor-Resources through an ancillary module kompendium-resources

The resources module contains two plugins: KompendiumResources and KompendiumResource. You will find more information on both below, but in a nutshell, the former is an application level plugin intended to define your entire application, while the latter is a route level approach should you wish to split out your route definitions.

Adding the Artifact

Prior to documenting your resources, you will need to add the artifact to your gradle build file.

dependencies {
  implementation("io.bkbn:kompendium-resources:$version")
}

NotarizedResources

The NotarizedResources plugin is an application level plugin, and must be installed after both the NotarizedApplication plugin and the Ktor Resources plugin. It is intended to be used to document your entire application in a single block.

private fun Application.mainModule() {
  install(Resources)
  install(NotarizedApplication()) {
    spec = baseSpec
  }
  install(NotarizedResources()) {
    resources = mapOf(
      Listing::class to NotarizedResources.ResourceMetadata(
        parameters = listOf(
          Parameter(
            name = "name",
            `in` = Parameter.Location.path,
            schema = TypeDefinition.STRING
          ),
          Parameter(
            name = "page",
            `in` = Parameter.Location.path,
            schema = TypeDefinition.INT
          )
        ),
        get = GetInfo.builder {
          summary("Get user by id")
          description("A very neat endpoint!")
          response {
            responseCode(HttpStatusCode.OK)
            responseType<ExampleResponse>()
            description("Will return whether or not the user is real 😱")
          }
        }
      ),
    )
  }
}

Here, the resources property is a map of KClass<*> to ResourceMetadata instance describing that resource. This metadata is functionally identical to how a standard NotarizedRoute is defined.

If you try to map a class that is not annotated with the ktor @Resource annotation, you will get a runtime exception!

NotarizedResource

If you prefer a route-based approach similar to NotarizedRoute, you can use the NotarizedResource<MyResourceType>() plugin instead of NotarizedResources. It will combine paths from any parent route with the route defined in the resource, exactly as Ktor itself does:

@Serializable
@Resource("/list/{name}/page/{page}")
data class Listing(val name: String, val page: Int)

private fun Application.mainModule() {
  install(Resources)
  route("/api") {
    listingDocumentation()
    get<Listing> { listing ->
      call.respondText("Listing ${listing.name}, page ${listing.page}")
    }
  }
}

private fun Route.listingDocumentation() {
  install(NotarizedResource<Listing>()) {
    parameters = listOf(
      Parameter(
        name = "name",
        `in` = Parameter.Location.path,
        schema = TypeDefinition.STRING
      ),
      Parameter(
        name = "page",
        `in` = Parameter.Location.path,
        schema = TypeDefinition.INT
      )
    )
    get = GetInfo.builder {
      summary("Get user by id")
      description("A very neat endpoint!")
      response {
        responseCode(HttpStatusCode.OK)
        responseType<ExampleResponse>()
        description("Will return whether or not the user is real 😱")
      }
    }
  }
}

In this case, the generated path will be /api/list/{name}/page/{page}, combining the route prefix with the path in the resource.

If you try to map a class that is not annotated with the ktor @Resource annotation, you will get a runtime exception!

Last updated