User Guide
This guide walks you through installing the tapik Gradle plugin, declaring endpoints with the Kotlin DSL, and running the generators that create clients, servers, and documentation.
|
New to tapik? Start with Getting Started for a step-by-step tutorial before diving into the full plugin reference below. |
Prerequisites
-
JDK compatible with the repository defaults (javaTargetVersion=21, toolchain javaToolchainVersion=24 in gradle.properties).
-
Gradle 9+ with the Kotlin DSL; configuration cache and task caching are supported out of the box.
-
Kotlin 2.2.20 (supplied via the Gradle toolchain).
-
Optional modules you plan to use, such as dev.akif.tapik:jackson for JSON bodies or Spring starters for generated code.
| Component | Example Dependency |
|---|---|
Endpoint DSL |
implementation("dev.akif.tapik:core:0.2.7") |
Codecs |
implementation("dev.akif.tapik:codec:0.2.7") |
Jackson integration |
implementation("dev.akif.tapik:jackson:0.2.7") |
Gradle plugin |
id("dev.akif.tapik.plugin.gradle") version "0.2.7" |
|
The version numbers match the repository snapshot (version=0.2.7). Replace them with the release you are consuming. |
Installing the Gradle Plugin
Configure the plugin in a module that produces HTTP endpoints or needs generated sources:
plugins {
kotlin("jvm")
id("dev.akif.tapik.plugin.gradle") version "0.2.7"
}
dependencies {
implementation("dev.akif.tapik:core:0.2.7")
implementation("dev.akif.tapik:jackson:0.2.7") // for JSON helpers
}
tapik {
basePackage("com.acme.catalog")
springRestClient { /* enables the Spring RestClient generator */ }
springWebMvc { /* enables the Spring WebMVC generator */ }
markdownDocumentation { /* enables the Markdown documentation generator */ }
}
Key things to remember:
-
basePackage must reference the package where your API implementations live. Only those classes (and their HttpEndpoint properties) are scanned.
-
The nested generator blocks are markers; you do not need to configure anything inside them unless future versions expose options.
-
Generated Kotlin sources land under build/generated/sources/tapik/main/kotlin and are added to the main source set automatically.
-
Non-source artefacts (for example, Markdown files) are written under build/generated.
Organizing endpoints
The scanner only looks at concrete types that implement API and at their val properties whose type is +HttpEndpoint<*, *, *>. That leads to a few practical conventions:
-
Keep the actual endpoint properties on objects or classes that implement API. Extension properties or interface properties that do not live on a concrete API implementor are not discovered.
-
To split definitions (read/write, bounded contexts) without multiplying generated clients, define helper functions in plain interfaces and use them from a single API object:
interface ReadUsersDefs { fun HttpEndpointVerbBuildingContext.configureGetEndpoint() = get("users" / path.uuid("id")) } interface WriteUsersDefs { fun HttpEndpointVerbBuildingContext.configureCreateEndpoint() = post("users") { /* ... */ } } object Users : API, ReadUsersDefs, WriteUsersDefs { val get by endpoint { configureGetEndpoint() } val create by endpoint { configureCreateEndpoint() } }This keeps one generated client/controller while letting you organize the DSL.
-
Nested API objects are supported and treated as separate modules (e.g., Users.Admin produces its own generated interfaces). If you want a single generated surface, keep helpers nested but avoid making them API; place endpoints only on the parent API implementor.
-
Multiple layers of nesting work the same way: every concrete API implementor yields its own generated outputs; nesting does not merge endpoints automatically.
Declaring Endpoints
Endpoints are compile-time constants built with the DSL in the core module. The DSL collects metadata from property names, documentation strings, and nested builders.
package com.acme.catalog.endpoints
import dev.akif.tapik.*
import dev.akif.tapik.jackson.jsonBody
// Implement API to gain access to the `endpoint` DSL.
object ProductEndpoints : API {
private val productId = path.uuid("productId")
private val locale = query.string("locale").optional("en-US")
val getProduct by endpoint(
description = "Fetch product details",
details = "Returns localized information when the locale query parameter is supplied."
) {
get("products" / productId + locale)
.input(header.uuid("X-Request-Id"))
.output(Status.OK) {
jsonBody<ProductView>("product")
}
.output(Status.NOT_FOUND) {
jsonBody<ProblemDetails>("problem")
}
}
}
Implementing API is the convention to expose the endpoint DSL and keep your endpoints discoverable as properties (e.g., ProductEndpoints.getProduct).
Highlights:
-
endpoint { ... } infers the endpoint id from the property name (getProduct). The description and details flow into generated KDoc and Markdown.
-
Path building uses operator overloads: "products" / productId.
-
Query parameters append with , headers with +input { ... }, and outputs use output(Status.OK) { ... }.
-
Response variants can be chained; the DSL supports OneOf unions to model branching behaviour.
Running Generation
tapik registers an aggregated tapikGenerate task. It depends on classes for the current project and its siblings to ensure bytecode is available.
./gradlew :service:tapikGenerate
Execution flow:
-
Compiled classes are scanned for properties that return HttpEndpoint.
-
Metadata describing the endpoints is persisted to build/generated/tapik-endpoints.txt.
-
The selected generators (spring-restclient, spring-webmvc, markdown-docs) run sequentially.
-
Generated sources are attached to the IDE/classpath; you can ./gradlew build without special configuration.
|
You can run ./gradlew :service:classes tapikGenerate during fast feedback loops. Pass -PskipLint if you want to avoid ktlint locally. |
Inspecting Outputs
-
Kotlin sources live under build/generated/sources/tapik/main/kotlin.
-
Markdown documentation is emitted to build/generated/API.md.
-
Scan report: build/generated/tapik-endpoints.txt contains the raw metadata list.
When checking generated Kotlin into source control:
-
Run ./gradlew tapikGenerate after any endpoint changes.
-
Review generated types as you would review hand-written code; they carry KDoc extracted from your endpoints.
-
For reproducibility, make sure the versions of tapik dependencies are pinned via a version catalog or BOM.
Troubleshooting
| Symptom | Common Cause | Fix |
|---|---|---|
Missing compiled classes warning |
classes task did not run before tapikGenerate |
Run ./gradlew classes tapikGenerate or wire tasks in your CI pipeline |
No generators execute |
Generator blocks omitted in the tapik extension |
Add springRestClient { }, springWebMvc { }, or markdownDocumentation { } |
Class not found during scan |
Class outside configured basePackage |
Ensure your API implementations live under the configured base package |
Unexpected HTTP mappings |
Endpoint path missing leading segment |
Start URIs with "segment" or "segment" / ... |
If you create custom generators, place their implementation on the build classpath and expose them via the Java ServiceLoader contract. The Gradle plugin will pick them up automatically when their id is enabled.