---
title: "Ktor Framework Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/kotlin-ktor-tutorial/"
markdown_url: "https://kotlin.dev.br/tutoriais/kotlin-ktor-tutorial.MD"
description: "Aprenda Ktor Framework em Kotlin com Exposed: crie APIs REST com routing, serialização, autenticação, banco de dados e dependências atualizadas."
date: "2025-07-18"
author: "Karina Melo"
---

# Ktor Framework Tutorial em Português — Passo a Passo | Kotlin Brasil

Aprenda Ktor Framework em Kotlin com Exposed: crie APIs REST com routing, serialização, autenticação, banco de dados e dependências atualizadas.


Neste tutorial, vamos construir uma API REST completa usando o **Ktor**, o framework web assíncrono e leve criado pela JetBrains especificamente para Kotlin. Você vai aprender desde a configuração inicial do projeto até a integração com banco de dados usando Exposed, passando por routing, serialização JSON, autenticação e testes. Se você quer aprofundar a parte de persistência depois deste guia, veja também o tutorial de [Kotlin com PostgreSQL no backend](/tutoriais/kotlin-postgresql-backend/). Se você já tem familiaridade com [funções](/glossario/fun/) e [coroutines](/glossario/coroutine/) em Kotlin, está pronto para começar.

## O que é o Ktor?

Ktor é um framework assíncrono para criar aplicações web e microsserviços em Kotlin. Diferente de frameworks como Spring Boot, o Ktor é minimalista por design — você adiciona apenas os recursos (chamados de **plugins**) que precisa. Ele é construído sobre coroutines, o que significa que cada requisição é tratada de forma não-bloqueante usando [suspend](/glossario/suspend/) functions.

A arquitetura do Ktor é baseada em um pipeline de plugins que interceptam e processam requisições HTTP. Isso torna o framework extremamente flexível e performático, ideal para microsserviços e APIs de alta concorrência.

## Passo 1: Configuração do Projeto

A forma mais rápida de iniciar um projeto Ktor é usando o gerador online em [start.ktor.io](https://start.ktor.io) ou configurando manualmente o `build.gradle.kts`. Vamos pelo caminho manual para entender cada dependência.

```kotlin
// build.gradle.kts
plugins {
    kotlin("jvm") version "2.0.0"
    id("io.ktor.plugin") version "2.3.12"
    kotlin("plugin.serialization") version "2.0.0"
}

group = "com.exemplo"
version = "1.0.0"

application {
    mainClass.set("com.exemplo.ApplicationKt")
}

dependencies {
    // Ktor Server
    implementation("io.ktor:ktor-server-core-jvm")
    implementation("io.ktor:ktor-server-netty-jvm")
    implementation("io.ktor:ktor-server-content-negotiation-jvm")
    implementation("io.ktor:ktor-serialization-kotlinx-json-jvm")
    implementation("io.ktor:ktor-server-auth-jvm")
    implementation("io.ktor:ktor-server-auth-jwt-jvm")

    // Exposed (ORM)
    implementation("org.jetbrains.exposed:exposed-core:0.52.0")
    implementation("org.jetbrains.exposed:exposed-dao:0.52.0")
    implementation("org.jetbrains.exposed:exposed-jdbc:0.52.0")
    implementation("com.h2database:h2:2.2.224")

    // Testes
    testImplementation("io.ktor:ktor-server-tests-jvm")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.0.0")
}
```

Observe que usamos o servidor **Netty** como engine. O Ktor suporta múltiplas engines (CIO, Jetty, Tomcat), mas Netty é a escolha mais comum para produção.

### Atenção às versões do Exposed em 2026

Muita gente chega a este tutorial procurando exatamente a dependência `org.jetbrains.exposed:exposed-core:0.52.0`. Ela aparece em vários projetos Kotlin antigos porque foi uma versão popular antes da estabilização do Exposed, mas hoje ela não deve ser copiada automaticamente para um projeto novo. Use o bloco acima como ponto de partida didático e, antes de criar um projeto de produção, confira o [guia atualizado do Exposed 1.0 com R2DBC](/blog/exposed-1-0-r2dbc-kotlin-2026/) e o [tutorial completo de Exposed ORM](/blog/kotlin-exposed-orm-framework-sql/).

A regra prática é simples:

- se o projeto usa JDBC tradicional, mantenha `exposed-core`, `exposed-dao`, `exposed-jdbc` e um driver como PostgreSQL ou H2;
- se a API precisa de acesso não bloqueante ponta a ponta, avalie `exposed-r2dbc` junto com Ktor, coroutines e um driver R2DBC;
- se o código é legado e já está preso em `0.52.0`, migre com testes, porque mudanças de pacote e comportamento podem aparecer ao subir para a linha 1.x;
- se você está aprendendo, priorize entender a modelagem de tabela, transações e repositórios antes de discutir micro-otimizações de versão.

Para este tutorial, o fluxo continua com JDBC porque ele é mais direto para iniciantes e funciona bem com H2 local, PostgreSQL em produção e deploys simples. Depois que a API estiver estável, você pode trocar a camada de persistência por R2DBC ou aprofundar no [guia de Kotlin para backend](/guias/kotlin-para-backend/) sem jogar fora o aprendizado de routing, serialização e testes.

## Passo 2: Criando o Servidor e Definindo Rotas

O ponto de entrada da aplicação Ktor é a função `embeddedServer`. Dentro dela, configuramos plugins e definimos rotas usando a DSL de routing.

```kotlin
// src/main/kotlin/com/exemplo/Application.kt
package com.exemplo

import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.http.*

fun main() {
    embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
        configurarRotas()
    }.start(wait = true)
}

fun Application.configurarRotas() {
    routing {
        get("/") {
            call.respondText("Bem-vindo à API Kotlin Brasil!", ContentType.Text.Plain)
        }

        route("/api/v1") {
            get("/status") {
                call.respondText("OK", ContentType.Text.Plain)
            }
        }
    }
}
```

A [DSL](/glossario/dsl/) de routing do Ktor utiliza [lambdas com receiver](/glossario/lambda/), permitindo definir rotas de forma declarativa e organizada. Cada bloco `get`, `post`, `put` e `delete` recebe o caminho da rota e uma [suspend](/glossario/suspend/) function que trata a requisição.

## Passo 3: Serialização JSON com kotlinx.serialization

Para trabalhar com JSON, precisamos instalar o plugin ContentNegotiation e configurar o serializador.

```kotlin
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable

@Serializable
data class Tarefa(
    val id: Int,
    val titulo: String,
    val concluida: Boolean = false
)

fun Application.configurarSerializacao() {
    install(ContentNegotiation) {
        json()
    }
}
```

Agora podemos criar endpoints que recebem e retornam objetos Kotlin automaticamente serializados como JSON. Veja como fica um CRUD básico de tarefas:

```kotlin
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.http.*

val tarefas = mutableListOf<Tarefa>()

fun Application.rotasTarefas() {
    routing {
        route("/api/tarefas") {
            get {
                call.respond(tarefas)
            }

            get("/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val tarefa = tarefas.find { it.id == id }
                if (tarefa != null) {
                    call.respond(tarefa)
                } else {
                    call.respond(HttpStatusCode.NotFound, mapOf("erro" to "Tarefa nao encontrada"))
                }
            }

            post {
                val tarefa = call.receive<Tarefa>()
                tarefas.add(tarefa)
                call.respond(HttpStatusCode.Created, tarefa)
            }

            delete("/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val removida = tarefas.removeIf { it.id == id }
                if (removida) {
                    call.respond(HttpStatusCode.NoContent)
                } else {
                    call.respond(HttpStatusCode.NotFound)
                }
            }
        }
    }
}
```

## Passo 4: Autenticação com JWT

O Ktor fornece plugins de autenticação prontos para uso. Vamos configurar autenticação via **JWT** (JSON Web Token), uma das abordagens mais comuns em APIs REST.

```kotlin
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm

fun Application.configurarAutenticacao() {
    val secret = "meu-segredo-super-secreto"
    val issuer = "kotlin-brasil"
    val audience = "api-usuarios"

    install(Authentication) {
        jwt("auth-jwt") {
            realm = "Acesso à API"
            verifier(
                JWT.require(Algorithm.HMAC256(secret))
                    .withAudience(audience)
                    .withIssuer(issuer)
                    .build()
            )
            validate { credential ->
                if (credential.payload.audience.contains(audience)) {
                    JWTPrincipal(credential.payload)
                } else null
            }
        }
    }

    routing {
        authenticate("auth-jwt") {
            get("/api/perfil") {
                val principal = call.principal<JWTPrincipal>()
                val email = principal!!.payload.getClaim("email").asString()
                call.respond(mapOf("email" to email))
            }
        }
    }
}
```

Rotas protegidas ficam dentro do bloco `authenticate`, garantindo que apenas requisições com token válido serão processadas.

## Passo 5: Integração com Banco de Dados usando Exposed

O **Exposed** é o ORM oficial da JetBrains para Kotlin. Ele oferece duas abordagens: DSL (estilo SQL) e DAO (estilo Active Record). Vamos usar a abordagem DSL.

```kotlin
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction

object Tarefas : Table("tarefas") {
    val id = integer("id").autoIncrement()
    val titulo = varchar("titulo", 255)
    val concluida = bool("concluida").default(false)
    override val primaryKey = PrimaryKey(id)
}

fun inicializarBanco() {
    Database.connect("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;", driver = "org.h2.Driver")
    transaction {
        SchemaUtils.create(Tarefas)
    }
}

fun listarTarefas(): List<Tarefa> = transaction {
    Tarefas.selectAll().map {
        Tarefa(
            id = it[Tarefas.id],
            titulo = it[Tarefas.titulo],
            concluida = it[Tarefas.concluida]
        )
    }
}

fun inserirTarefa(tarefa: Tarefa): Int = transaction {
    Tarefas.insert {
        it[titulo] = tarefa.titulo
        it[concluida] = tarefa.concluida
    }[Tarefas.id]
}
```

Para integrar com o Ktor, chame `inicializarBanco()` no módulo da aplicação e substitua a lista em memória pelas funções do Exposed nas rotas.

## Passo 6: Testando a Aplicação Ktor

O Ktor inclui um módulo de testes que permite simular requisições HTTP sem iniciar um servidor real, usando `testApplication`.

```kotlin
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.testing.*
import kotlin.test.*

class TarefasTest {
    @Test
    fun `deve retornar status OK`() = testApplication {
        application {
            configurarSerializacao()
            configurarRotas()
        }

        client.get("/api/v1/status").apply {
            assertEquals(HttpStatusCode.OK, status)
            assertEquals("OK", bodyAsText())
        }
    }

    @Test
    fun `deve criar tarefa via POST`() = testApplication {
        application {
            configurarSerializacao()
            rotasTarefas()
        }

        client.post("/api/tarefas") {
            contentType(ContentType.Application.Json)
            setBody("""{"id": 1, "titulo": "Estudar Ktor", "concluida": false}""")
        }.apply {
            assertEquals(HttpStatusCode.Created, status)
        }
    }
}
```

O `testApplication` cria um ambiente isolado onde você pode testar cada módulo da sua aplicação independentemente, sem necessidade de subir o servidor Netty.

## Deploy Básico

Para gerar um JAR executável, configure o plugin `ktor` no Gradle e execute:

```kotlin
// build.gradle.kts
ktor {
    fatJar {
        archiveFileName.set("app.jar")
    }
}
```

Depois, basta rodar `./gradlew buildFatJar` e executar com `java -jar build/libs/app.jar`. Para containerizar, crie um `Dockerfile` simples baseado em uma imagem JVM como `eclipse-temurin:21-jre-alpine`.

## Erros Comuns

**1. Esquecer de instalar o plugin ContentNegotiation:** Sem ele, o Ktor não sabe serializar objetos para JSON e retorna erro 500. Sempre chame `install(ContentNegotiation) { json() }` antes de usar `call.respond` com [data classes](/glossario/data-class/).

**2. Não tratar parâmetros nulos nas rotas:** `call.parameters["id"]` retorna [nullable](/glossario/nullable/) `String?`. Sempre use `toIntOrNull()` e trate o caso nulo para evitar exceções em runtime.

**3. Bloquear a thread principal com operações de banco:** O Exposed usa JDBC, que é bloqueante. Em produção, envolva as chamadas em `newSuspendedTransaction` do módulo `exposed-kotlin` ou use `withContext(Dispatchers.IO)` para não bloquear as coroutines do Ktor.

**4. Não configurar CORS:** Se sua API será consumida por um frontend em outro domínio, instale o plugin `CORS` do Ktor. Sem ele, o navegador bloqueará as requisições.

**5. JWT secret hardcoded no código:** Nunca deixe secrets diretamente no código fonte. Use variáveis de ambiente com `System.getenv("JWT_SECRET")` ou arquivos de configuração do Ktor (`application.conf`).

## Conclusão e Próximos Passos

Neste tutorial, construímos uma API REST funcional com Ktor cobrindo os pilares fundamentais: routing, serialização, autenticação JWT, integração com banco de dados Exposed e testes automatizados. O Ktor é uma excelente escolha para quem quer construir backends em Kotlin puro, aproveitando ao máximo as [coroutines](/glossario/coroutine/) e a expressividade da linguagem.

Como próximos passos, recomendamos explorar o **Ktor Client** para fazer requisições HTTP a outros serviços, configurar [WebSockets com Ktor](/tutoriais/ktor-websockets-kotlin/) para comunicação em tempo real, integrar com bancos de dados de produção como PostgreSQL e documentar os contratos HTTP. Se a API será consumida por frontend, app mobile ou outro serviço, siga o tutorial de [OpenAPI e Swagger no Ktor](/tutoriais/kotlin-ktor-openapi-swagger/) para expor schemas, exemplos e códigos de erro de forma profissional. Você também pode estudar o uso de [Koin](/glossario/delegation/) ou Kodein para injeção de dependências, tornando sua aplicação mais modular e testável.

## FAQ rápido sobre Ktor, Exposed e dependências

### Posso usar `org.jetbrains.exposed:exposed-core:0.52.0` em um projeto novo?

Pode, mas não é a melhor escolha para um projeto novo em 2026. A versão `0.52.0` ainda aparece em exemplos, buscas e bases antigas, mas o ecossistema Exposed avançou. Para código novo, consulte a versão estável atual no Maven Central e leia o [resumo do Exposed 1.0](/blog/exposed-1-0-r2dbc-kotlin-2026/) antes de fixar dependências.

### Ktor combina melhor com Exposed JDBC ou R2DBC?

Para aprender e entregar uma API pequena, JDBC com HikariCP é o caminho mais simples. Para serviços com muitas conexões simultâneas, streaming ou uma arquitetura já reativa, R2DBC pode fazer sentido. A decisão não deve ser tomada só porque Ktor usa coroutines; o banco, o driver, o pool e a equipe também importam.

### Preciso de Spring Boot para usar Kotlin no backend?

Não. Spring Boot é excelente para equipes que já vivem no ecossistema Spring, mas Ktor é mais leve, direto e Kotlin-first. Se você ainda está comparando frameworks, leia também [Ktor vs Spring Boot](/comparacoes/ktor-vs-spring-boot/) e o [guia de frameworks server-side Kotlin](/guias/kotlin-server-side/).

### Qual é o próximo passo depois deste tutorial?

Transforme a API de exemplo em um projeto de portfólio: adicione autenticação real, migrations, testes de integração, Docker e um pipeline de CI. O [roadmap de backend Kotlin](/carreira/roadmap-dev-backend-kotlin/) mostra a sequência ideal para sair do tutorial e chegar em projetos que ajudam em entrevistas.

Para aprofundar seus conhecimentos em Kotlin para backend, confira nosso tutorial sobre [Coroutines Avançadas](/tutoriais/coroutines-avançado/) e o guia [Kotlin para Backend](/guias/kotlin-para-backend/). Se você trabalha com múltiplas linguagens no backend, compare a abordagem do Ktor com <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">frameworks Go como Gin e Fiber</a> e com <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">FastAPI em Python para APIs modernas</a>.
