Ktorfit Documentation
repository·master·Indexed 24 days ago
https://github.com/foso/ktorfitA Kotlin Multiplatform HTTP client library that uses KSP to generate type-safe implementations of API interfaces, similar to Retrofit. It supports Android, iOS, JS, JVM, and Linux, utilizing Ktor clients and a compiler plugin to automate the instantiation of API implementation classes.
What's inside Ktorfit
- Ktorfit is an HTTP client and Kotlin Symbol Processor (KSP) designed for Kotlin Multiplatform (KMP) projects. It supports Android, iOS, JS, JVM, and Linux. Inspired by Retrofit, it uses Ktor clients and KSP to generate type-safe API implementations from interfaces.
Understand the Ktorfit project structure
masterThe Ktorfit repository is organized into several modules. Understanding these helps in locating specific parts of the library or finding examples:
compiler plugin: Source code for the compiler plugin.ktorfit-annotations: Module containing the annotations used by Ktorfit.ktorfit-ksp: Source code for the KSP (Kotlin Symbol Processing) plugin.ktorfit-lib-core: Core logic for the Ktorfit library.ktorfit-lib: Combinesktorfit-lib-corewith dependencies on platform-specific clients.sandbox: Experimental module for testing new features.example: Contains sample projects demonstrating how to use Ktorfit.docs: Source files for the GitHub documentation page.
Send Multipart data using @Body or @MultiPart
masterThere are two ways to send multipart data in Ktorfit:
Option 1: Using @Body
Pass a parameter of type
MultiPartFormDataContentannotated with@Body. You can construct this using Ktor'sformDatabuilder.Option 2: Using @MultiPart
Annotate the function with
@Multipart. Individual parts are defined using the@Partannotation. All@Partparameters are combined into a singleMultiPartFormDataContentrequest.// Option 1: @Body interface ExampleService { @POST("upload") suspend fun upload(@Body map: MultiPartFormDataContent) } // Option 2: @MultiPart @Multipart @POST("upload") suspend fun uploadFile(@Part("description") description: String, @Part("") file: List<PartData>): StringHow Ktorfit works under the hood
masterKtorfit operates using three main components that work together during the build process to turn annotated interfaces into functional API clients:
- KSP-Plugin: Scans your interfaces for Ktorfit annotations (like
@GET) and generates the implementation classes. - Compiler Plugin: A Gradle-integrated plugin that transforms calls to the
create()function to inject the generated implementation. - Ktorfit lib: A wrapper around Ktor that provides the runtime infrastructure and simplifies code generation.
The Generation Lifecycle
When you define an interface like
ExampleApi, KSP generates a class named_ExampleApiImplin the same package. It also generates aClassProviderand an extension functioncreateExampleApi()to facilitate instantiation.package com.example import com.example.model.People import de.jensklingenberg.ktorfit.http.GET interface ExampleApi { @GET("/test") suspend fun exampleGet(): People }- KSP-Plugin: Scans your interfaces for Ktorfit annotations (like
How the Ktorfit compiler plugin works
masterThe Ktorfit compiler plugin automates the instantiation of API implementation classes. It intercepts calls to thecreatefunction fromKtorfit-liband automatically injects the generated implementation class as an argument. The plugin uses the type parameter provided to thecreatefunction to deduce the correct implementation class name (e.g., if you passExampleApi, it looks for_ExampleApiImpl).How the Ktorfit create() function is transformed
masterKtorfit uses a compiler plugin to automate the instantiation of API implementations. When you call the generic
create<T>()function, the compiler plugin intercepts this call and replaces it with a call that provides the specific generated implementation class as an argument.Original Code:
val api = jvmKtorfit.create<ExampleApi>()Transformed Code (at compile time):
val api = jvmKtorfit.create<ExampleApi>(_ExampleApiImpl(jvmKtorfit))If the compiler plugin is not correctly applied (e.g., the Gradle plugin is missing), the
create()function will throw anIllegalArgumentExceptionwith the messageENABLE_GRADLE_PLUGINbecause it expects the defaultnullvalue to be replaced by the plugin.public fun <T> create(data: T? = null): T { if (data == null) { throw IllegalArgumentException(ENABLE_GRADLE_PLUGIN) } return data }How converters work in Ktorfit
masterConverters are used to transform HTTP responses or request parameters. To use a converter, you must wrap it in a
Converter.Factoryand register that factory with the Ktorfit builder using theconverterfactories()function.There are three main types of converters:
- ResponseConverters: For converting HTTP responses.
- SuspendResponseConverter: For converting HTTP responses in
suspendfunctions. - RequestParameterConverter: For converting request parameters.
Understand the scope and design goals of Ktorfit
masterKtorfit is designed to provide a developer experience similar to Retrofit for Kotlin Multiplatform (KMP) projects.
Key architectural decisions include:
- Ktor-based: Instead of being a 100% drop-in replacement for Retrofit, Ktorfit uses Ktor clients as its engine because Ktor is available on nearly every KMP compile target.
- Platform Compatibility: Every feature is implemented to ensure compatibility across all platforms supported by Ktor.
- Ktor Integration: Before adding new functionality, Ktorfit evaluates if an existing Ktor plugin can solve the same problem, ensuring it stays aligned with the Ktor ecosystem.
Understand how `@NoDelegation` affects generated code
masterWhen using
@NoDelegation, the Ktorfit compiler plugin changes how the implementation class is structured.For a regular interface (without the annotation), Ktorfit generates a delegation clause (e.g.,
by _SuperTestService1Impl(_ktorfit)). For an interface marked with@NoDelegation, this delegation clause is omitted for that specific interface in the generated class.// Example of what is generated for TestService: public class _TestServiceImpl( private val _ktorfit: Ktorfit, ) : TestService, SuperTestService1 by com.example.api._SuperTestService1Impl(_ktorfit) { // No delegation for SuperTestService2 because it was marked @NoDelegation }When to use ResponseConverters
masterUseResponseConverteronly when you cannot use asuspendfunction in your interface. Because Ktor relies on Coroutines by default, Ktorfit functions typically require thesuspendmodifier. For most standard use cases, you should useSuspendResponseConverterinstead of aResponseConverter.Migrate SuspendResponseConverter to Converter.SuspendResponseConverter
masterWhen implementing custom converters, the
SuspendResponseConverterinterface has been renamed toConverter.SuspendResponseConverter.If you are implementing a
Converter.Factory, you should now use thesuspendResponseConvertermethod to return an instance ofConverter.SuspendResponseConverter.public class CallConverterFactory : Converter.Factory { override fun suspendResponseConverter( typeData: TypeData, ktorfit: Ktorfit ): Converter.SuspendResponseConverter<HttpResponse, *>? { // implementation } }Migrate from Call? to 2.0.0+
masterIn Ktorfit versions 2.0.0 and later, if you useCall?as a return type in your API interfaces, you must explicitly add thektorfit-converters-calldependency and register theCallConverterFactoryin your Ktorfit instance.