I'm building a JS and JVM library for a REST API; ...
# multiplatform
b
I'm building a JS and JVM library for a REST API; when I instantiate the client, I would like the user to be able to pass a KTOR HTTP Client instance to override behavior or add additional stuff (like request interceptors) but the KTOR client itself can't be exported to JS; what's a common way to deal with that? create an adapter around the client and duplicate all configuration options?
h
IMHO it depends on your library. Do you only want to expose a REST api or do you want to create higher level library? For the first one, like creating Ktor SDK for a OpenAPI file, I create the following functions:
Copy code
public suspend fun HttpClient.bazA(
  input: FooInput,
  X_CSRF_Token: String,
  B: String? = null,
  builder: suspend HttpRequestBuilder.() -> Unit = {},
): BazAResult {
https://github.com/hfhbd/kfx/blob/main/openapi-fir/src/a/kotlin/client/bazA.kt and for setup:
Copy code
fun <T : HttpClientEngineConfig> HttpClientConfig<T>.setupJiraClient() {
    expectSuccess = true
    install(ContentNegotiation) {
        setupJiraJson()
    }
}

fun Configuration.setupJiraJson() {
    jsonIo(
        Json {
            useAlternativeNames = false
            ignoreUnknownKeys = true
        },
    )
}
b
idea is to expose something like
Copy code
@JsExport
class ProductClient(
    private val configuration: Configuration,
) {
    private val tenant = configuration.tenant

    suspend fun createProduct(
        product: CreateProduct,
        token: String,
        skipVariantGeneration: Boolean = false,
        doIndex: Boolean = false,
        contentLanguage: String? = null,
    ) {
        val result = <http://client.post|client.post>(configuration.endpoint) {
            url {
                appendPathSegments("product", tenant, "products")
                parameters {
                    if (doIndex) {
                        append("doIndex", "true")
                    }
                    if (skipVariantGeneration) {
                        append("skipVariantGeneration", "true")
                    }
                }
            }
            contentType(ContentType.Application.Json)
            headers {
                contentLanguage?.let {
                    append(HttpHeaders.ContentLanguage, it)
                }
                bearerAuth(token)
                setBody(product)
            }
        }
    }
}
obviously I could instantiate the client inside the API, but I'm not sure if that would be super ergonomic to use from Kotlin
but I'd probably need a wrapper around that for JS that can be exported
hm, this works
Copy code
@JsExport
class ProductApi(private val config: ApiConfiguration): IProductClient by ProductClient(Configuration(
    client = HttpClient {
        install(ContentNegotiation) {
            json(Json {
                explicitNulls = false
            })

        }
    },
    endpoint = config.endpoint,
    tenant = config.tenant,
))

@JsPlainObject
external interface ApiConfiguration {
    val endpoint: String
    val tenant: String
}
pulling out the client api into an interface, then use delegation