Getting started

This page takes a project from nothing to a client calling a server, both generated from one declaration. It uses three modules: shared, server and client. The exact build wiring of each one is on Setup, which also covers the platforms that are not the JVM.

  1. Set up the shared, server and client modules as described in Setup.

  2. Declare a resource and a service in shared:

    shared/src/commonMain/kotlin/endpoints.kt
    @Endpoint.Resource
    interface Articles
    
    @Endpoint.Service
    interface ArticlesService : EndpointService {
        suspend fun Articles.get(@Endpoint.Query page: Int? = null): Either<List<String>?, NoFail> (1)
    }
    1 A GET /articles?page, whose optional page is omitted from the URL when it is null.

    The Articles resource is a route, and each suspend function that extends it is one endpoint.

  3. Implement the service in server, and mount it:

    server/src/main/kotlin/main.kt
    @ServerEndpointService("articles") (1)
    class ServerArticlesService : ArticlesService {
        override suspend fun Articles.get(page: Int?): Either<List<String>?, NoFail> =
            Either.value(listOf("Hello", "World"))
    }
    
    fun main() {
        embeddedServer(CIO, port = 8080) {
            install(ContentNegotiation) { json() } (2)
            routing { articles(ServerArticlesService()) } (3)
        }.start(wait = true)
    }
    1 The generated Route.articles extension is named after this argument.
    2 Nomi leaves the serialization format to ktor.
    3 The generated extension registers every endpoint of the service.
  4. Call it from client:

    client/src/commonMain/kotlin/main.kt
    @ClientEndpointServiceFactory(ArticlesService::class, "articles") (1)
    object APIServices
    
    suspend fun main() {
        val client = HttpClient {
            install(ContentNegotiation) { json() } (2)
            defaultRequest {
                url("http://localhost:8080/")
                contentType(ContentType.Application.Json)
            }
        }
    
        val result = APIServices.articles(client).call { Articles().get(page = 1) } (3)
        println(result)
    }
    1 Generates APIServices.articles(client).
    2 The client must use the same format as the server.
    3 The call reads like the route it addresses.

    The result is a CallStatus.Result, which tells a success from a business failure, a server failure and a network failure.

That’s it! From here, Resources explains how routes are declared, and CallStatus how to read a result.