Skip to content

Clients

Misk provides configurable HTTP clients built on top of OkHttp and Retrofit, and gRPC clients built on top of Wire.

HTTP Clients

Config

Set up a Config object (and use MiskConfig.load to load config from a YAML file):

data class MyServiceConfig(
  val http_clients: HttpClientsConfig,
  // ...
) : Config

In the configuration YAML, specify the target service’s address:

http_clients:
  ...
  endpoints:
    ...
    greeter:  { url: "https://hello.example.com" }

Create an OkHttpClient

Use HttpClientModule:

class MyClientModule : KAbstractModule() {
  override fun configure() {
    install(HttpClientModule(
      // Corresponds to the YAML config. Requires a bound HttpClientsConfig
      name = "greeter",
      // Optional annotation to define how you inject your client dependency
      annotation = Names.named("greeterHttp")
    ))
  }
}

This binds an OkHttpClient that you can inject:

class MyClient @Inject constructor(@Named("greeterHttp") val client: OkHttpClient) {
  fun callGreeter() {
    val response = client.newCall(
      Request.Builder()
        .url("http://localhost:8080/hello")
        .build()
    ).execute()
  }
}

Create typed clients with Retrofit

First, create a Retrofit interface. See the Retrofit docs for more details.

interface GreeterApi {
  @POST("/hello")
  @Headers(value = ["accept: application/json"])
  fun hello(
    @Body request: HelloRequest
  ): Call<HelloResponse>
}

Next, install a TypedHttpClientModule with this interface.

class HelloClientModule : KAbstractModule() {
  override fun configure() {
    install(
      TypedHttpClientModule(
        GreeterApi::class, 
        // Corresponds to the YAML config. Requires a bound HttpClientsConfig
        name = "greeter", 
        // Optional annotation to define how you inject your client dependency
        annotation = Names.named("greeterApi")
      )
    )
  }
}

Now you can inject an implementation of this client:

@Singleton class MyApiClient @Inject constructor(
  @Named("greeterApi") private val api: GreeterApi
) {
  fun hello(message: String): String {
    val response = api.hello(
      HelloRequest(
        message = message
      )
    ).execute()
  }
}

gRPC Clients

First, include the auto-generated gRPC client code at the caller module using the Wire Gradle plugin.

plugins {
  id("com.squareup.wire")
}

wire {
  sourcePath {
    srcDir("src/main/proto")
  }

  // Generate Kotlin for the gRPC client API.
  kotlin {
    // Set this to false if you're generating client and server interfaces in one module
    exclusive = false
    includes ("squareup.cash.hello.GreeterService")
    rpcRole = "client"
  }

  java {
  }
}

Next, bind your client in code in a similar fashion to an HTTP client. Set up client configuration, as described in Config. Then, bind a GrpcClientModule:

class GreeterClientModule : KAbstractModule() {
  override fun configure() {
    install(GrpcClientModule.create<GreeterServiceClient, GrpcGreeterServiceClient>(
        // Corresponds to the YAML config. Requires a bound HttpClientsConfig
        name = "greeter",
        // Optional annotation to define how you inject your client dependency
        annotation = Names.named("greeterGrpc")
    ))
  }
}

With this all setup, you can now inject your client in source code and connect via gRPC:

internal class GrpcGreeterServiceClient @Inject internal constructor(
  @Named("greeterGrpc") private val greeterGrpc: GreeterServiceClient
) {
  fun get(message: String) {
    val response = greeterGrpc.Hello().executeBlocking(HelloRequest(message))

    // ... do something with the response here
  }
}