O Ktor é o framework HTTP nativo do ecossistema Kotlin, mantido pela JetBrains, e a versão 3.4.0 chegou com melhorias que impactam diretamente quem desenvolve APIs, microsserviços e aplicações em tempo real. Se você já usa Ktor para criar APIs ou está planejando migrar do Spring Boot, este artigo cobre tudo que mudou e como aproveitar na prática.

Neste guia, vamos explorar as principais novidades: geração de documentação OpenAPI diretamente do código, streaming duplex com OkHttp, compressão Zstd, o novo plugin de ciclo de vida de requisições e várias outras melhorias.

Geração de OpenAPI direto do código

Uma das features mais pedidas pela comunidade finalmente chegou ao Ktor 3.4: a capacidade de gerar documentação OpenAPI dinamicamente a partir do código de roteamento, sem precisar manter um arquivo YAML ou JSON separado.

A abordagem anterior exigia manter um arquivo openapi.yaml sincronizado manualmente com as rotas — um pesadelo de manutenção em projetos grandes. Agora, com o novo compiler plugin, a documentação vive junto do código:

import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.http.*

fun Route.messageRoutes() {
    get("/messages") {
        val query = call.parameters["search"]?.parseQueryOrNull()
        call.respond(messageTable.listMessages(query))
    }.describe {
        summary = "Lista todas as mensagens"
        description = "Retorna uma lista paginada de mensagens com filtro opcional."
        parameters {
            query("search") {
                description = "Termo de busca para filtrar mensagens"
                required = false
            }
            query("page") {
                description = "Número da página (começando em 0)"
                required = false
            }
        }
        responses {
            HttpStatusCode.OK {
                description = "Lista de mensagens retornada com sucesso"
                schema = jsonSchema<List<Message>>()
            }
            HttpStatusCode.BadRequest {
                description = "Parâmetros de busca inválidos"
            }
        }
    }

    post("/messages") {
        val message = call.receive<CreateMessageRequest>()
        val created = messageTable.create(message)
        call.respond(HttpStatusCode.Created, created)
    }.describe {
        summary = "Cria uma nova mensagem"
        requestBody {
            schema = jsonSchema<CreateMessageRequest>()
        }
        responses {
            HttpStatusCode.Created {
                schema = jsonSchema<Message>()
            }
        }
    }
}

A função describe é uma extension function que se encaixa naturalmente no DSL de roteamento do Ktor. A documentação OpenAPI é gerada em runtime e pode ser exposta via endpoint dedicado, facilitando integração com Swagger UI ou outros clientes.

Se você vem do Spring Boot, essa abordagem é similar ao SpringDoc, mas sem annotations — tudo via DSL Kotlin idiomático.

Streaming duplex com OkHttp

O Ktor 3.4 trouxe suporte a streaming duplex no cliente OkHttp, permitindo enviar dados no corpo da requisição e receber dados da resposta simultaneamente via HTTP/2. Isso é essencial para cenários como:

  • Upload de arquivos grandes com feedback de progresso em tempo real
  • Comunicação bidirecional sem WebSocket
  • Streaming de dados de IA (como respostas de LLMs)

A configuração é simples:

import io.ktor.client.*
import io.ktor.client.engine.okhttp.*

val client = HttpClient(OkHttp) {
    engine {
        duplexStreamingEnabled = true
    }
}

Com o duplex habilitado, você pode criar fluxos bidirecionais:

client.preparePost("https://api.exemplo.com/stream") {
    setBody(object : OutgoingContent.WriteChannelContent() {
        override suspend fun writeTo(channel: ByteWriteChannel) {
            // Envia dados progressivamente
            repeat(100) { i ->
                channel.writeStringUtf8("chunk-$i\n")
                delay(100)
            }
        }
    })
}.execute { response ->
    // Recebe dados enquanto ainda está enviando
    val channel = response.bodyAsChannel()
    while (!channel.isClosedForRead) {
        val line = channel.readUTF8Line() ?: break
        println("Recebido: $line")
    }
}

Essa feature é especialmente útil se você trabalha com gRPC e Kotlin, já que o streaming bidirecional é um padrão comum em comunicação entre microsserviços.

Compressão Zstd

O Ktor 3.4 adicionou suporte nativo ao Zstd (Zstandard), o algoritmo de compressão desenvolvido pelo Facebook que oferece taxas de compressão superiores ao gzip com velocidade de descompressão significativamente maior.

Para habilitar no servidor, basta instalar o módulo dedicado:

import io.ktor.server.plugins.compression.*

fun Application.configureCompression() {
    install(Compression) {
        zstd {
            compressionLevel = 3 // Padrão: bom equilíbrio velocidade/compressão
        }
        gzip {
            priority = 0.9
        }
        deflate {
            priority = 0.5
        }
    }
}

O Zstd brilha em cenários onde você transmite payloads JSON grandes — como listas de produtos, logs ou dados analíticos. Em benchmarks internos da JetBrains, o Zstd com nível 3 comprime 20-30% melhor que gzip com velocidade de descompressão até 5x superior.

A dependência é separada para não inflar projetos que não precisam:

// build.gradle.kts
dependencies {
    implementation("io.ktor:ktor-server-compression-zstd:3.4.0")
}

Plugin HttpRequestLifecycle

O novo plugin HttpRequestLifecycle resolve um problema antigo: o que acontece com requisições em processamento quando o cliente desconecta? Antes do Ktor 3.4, a coroutine continuava executando até o final, desperdiçando recursos.

Agora, com concorrência estruturada integrada ao ciclo de vida da requisição, operações em andamento são canceladas automaticamente:

import io.ktor.server.plugins.httprequestlifecycle.*

fun Application.configureLifecycle() {
    install(HttpRequestLifecycle)

    routing {
        get("/relatorio-pesado") {
            // Se o cliente desconectar, esta coroutine é cancelada
            val dados = withContext(Dispatchers.IO) {
                // Consulta demorada ao banco de dados
                repository.gerarRelatorioCompleto()
            }
            call.respond(dados)
        }
    }
}

Essa feature é particularmente importante para operações que consomem muitos recursos, como geração de relatórios, processamento de imagens ou consultas complexas ao banco. Se você trabalha com observabilidade em Kotlin, o cancelamento adequado também gera métricas mais precisas sobre latência real.

Melhorias em Server-Sent Events

O suporte a SSE (Server-Sent Events) que foi introduzido no Ktor 3.0 recebeu melhorias de estabilidade e desempenho no 3.4. O SSE é ideal para cenários onde o servidor precisa enviar atualizações unidirecionais ao cliente — como notificações, feeds de dados ao vivo e dashboards:

import io.ktor.server.sse.*
import io.ktor.sse.*

fun Application.configureSSE() {
    install(SSE)

    routing {
        sse("/eventos") {
            // Envia eventos a cada segundo
            var contador = 0
            while (true) {
                send(ServerSentEvent(
                    data = """{"tipo": "atualizacao", "valor": ${contador++}}""",
                    event = "dados",
                    id = contador.toString()
                ))
                delay(1000)
            }
        }
    }
}

No lado do cliente, o Ktor também oferece suporte nativo:

val client = HttpClient(CIO)

client.sse("https://api.exemplo.com/eventos") {
    incoming.collect { evento ->
        println("Evento recebido: ${evento.data}")
    }
}

Se você está considerando entre SSE e WebSocket, o SSE é mais simples quando a comunicação é unidirecional (servidor para cliente) e funciona melhor com proxies e load balancers tradicionais.

Migração e compatibilidade

O Ktor 3.4 é totalmente compatível com o Kotlin 2.3.20 e funciona com o Kotlin 2.4.0 Beta sem problemas. A migração a partir do Ktor 3.x anterior é direta — basta atualizar a versão no Gradle:

// build.gradle.kts
val ktor_version = "3.4.0"

dependencies {
    implementation("io.ktor:ktor-server-core:$ktor_version")
    implementation("io.ktor:ktor-server-netty:$ktor_version")
    implementation("io.ktor:ktor-server-content-negotiation:$ktor_version")
    implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
}

Se você vem do Ktor 2.x, a migração é mais significativa devido à troca do kotlinx-io. Consulte o guia de backend com Ktor para mais detalhes sobre a arquitetura recomendada.

Conclusão

O Ktor 3.4 consolida o framework como uma opção séria para backend em Kotlin, especialmente para equipes que valorizam a abordagem idiomática do Kotlin com coroutines e DSLs. A geração de OpenAPI via código elimina uma dor antiga, o streaming duplex abre novas possibilidades para aplicações em tempo real, e o Zstd melhora a performance de transferência de dados.

Se você está avaliando frameworks para backend em Kotlin, vale conferir a comparação entre Ktor e Spring Boot para entender qual se encaixa melhor no seu cenário.

Para quem trabalha com outras linguagens no backend, é interessante comparar a abordagem do Ktor com frameworks de Go, que também prioriza simplicidade e performance, ou com o ecossistema de Python para APIs com FastAPI — cada linguagem traz suas vantagens dependendo do caso de uso.