Spring RestClient Clients¶
The spring-restclient generator produces Kotlin interfaces that call HTTP endpoints through the Spring Framework RestClient API. Each generated interface groups endpoints by the source file that defined them and extends dev.akif.tapik.spring.restclient.RestClientBasedClient.
Generated Structure¶
- Location:
build/generated/sources/tapik/main/kotlin/<package>/<SourceFile>Client.kt - Interface name:
<SourceFile>Client - Base interface:
RestClientBasedClient, which exposes: val restClient: RestClientval interpreter: RestClientInterpreter(lazy wrapper aroundrestClient)- Method signature: one function per endpoint property, using descriptive parameter names derived from the DSL.
Example excerpt:
interface ProductEndpointsClient : RestClientBasedClient {
/**
* Fetch product details.
*/
fun getProduct(
productId: UUID,
locale: String = "en-US",
xRequestId: UUID
): Response0<ProductView> {
val responseEntity = interpreter.send(
method = ProductEndpoints.getProduct.method,
uri = ProductEndpoints.getProduct.toURI(productId, locale),
inputHeaders = ProductEndpoints.getProduct.input.encodeInputHeaders(xRequestId),
inputBodyContentType = ProductEndpoints.getProduct.input.body.mediaType,
inputBody = ByteArray(0),
outputs = ProductEndpoints.getProduct.outputs.toList()
)
val status = responseEntity.statusCode.toStatus()
val bodyBytes = responseEntity.body ?: ByteArray(0)
val decodedBody = ProductEndpoints.getProduct.outputs.item1.body.codec.decode(bodyBytes)
.getOrElse { error(it.joinToString(": ")) }
return Response0(status, decodedBody)
}
}
Note
Names and types are inferred from your endpoint declarations: path/query parameters become function parameters, headers with codecs translate into arguments, and bodies map to encoded values.
Return Types¶
| Endpoint outputs | Generated return type |
|---|---|
| Single body without headers | Response0<T> |
| Single body with headers | ResponseN<T, Header1, …> (where N matches the header arity) |
| No body, headers only | ResponseWithoutBodyN<Header1, …> |
| Multiple outputs | OneOfK<…> wrapping the corresponding ResponseX type |
You can pattern match on OneOfK (provided by tapik’s core module) to branch on status-based responses.
Header and Body Handling¶
- Headers are encoded via the
Input.encodeInputHeadershelpers fromcore; responses use thedecodeHeadersXutilities to validate and decode header values. - Bodies use the codec attached to the
Bodydefinition (for example, Jackson-based JSON codecs). AnyCodecFailuresurfaces as anIllegalStateExceptionin generated code so mismatches fail fast. RestClientInterpretervalidates response status codes and media types before the generator attempts to decode bodies, surfacing detailedRestClientResponseExceptionmessages when mismatches occur.
Instantiating Clients¶
Create a Spring-managed implementation by delegating to the generated interface:
@Component
class HttpProductClient(
override val restClient: RestClient
) : ProductEndpointsClient
RestClientBasedClient provides interpreter automatically, so implementations only supply the RestClient. You may inject a configured RestClient.Builder or reuse the application-wide client.
Testing Tips¶
- Drive endpoint behaviour through unit tests on the DSL before running code generation.
- Use Spring's
MockRestServiceServerto verify generated clients: intercept outgoing requests, assert the expected path/headers/body, and feed back fixtures that satisfy your endpoint outputs. - If you customise codecs, add integration tests to ensure
RestClientInterpretercan successfully decode the wire format.
Customisation Hooks¶
- Provide your own
RestClient(set timeouts, interceptors, authentication) while reusing generated code. - Override functions in the generated interface by writing extension functions or wrapping the generated interface inside a higher-level service.
- Add new generators for other HTTP client stacks by following the steps in Code Generation Overview; the Spring client serves as a realistic template.