---
title: "Ktor: Criando APIs REST com Kotlin | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/ktor-criando-apis-kotlin/"
markdown_url: "https://kotlin.dev.br/blog/ktor-criando-apis-kotlin.MD"
description: "Aprenda a criar APIs REST com Ktor e Kotlin. Tutorial completo em português com exemplos de CRUD, plugins e deploy para produção."
date: "2026-03-23"
author: "Karina Melo"
---

# Ktor: Criando APIs REST com Kotlin | Kotlin Brasil

Aprenda a criar APIs REST com Ktor e Kotlin. Tutorial completo em português com exemplos de CRUD, plugins e deploy para produção.


Se você procura um framework leve, idiomático e 100% Kotlin para construir APIs REST, o Ktor é a escolha certa. Criado pela JetBrains (a mesma empresa por trás do Kotlin), o Ktor aproveita o melhor da linguagem: coroutines nativas, DSLs elegantes e tipagem forte. Neste tutorial, vamos construir uma API REST completa do zero.

## O que é o Ktor?

Ktor é um framework assíncrono para criar aplicações conectadas — servidores HTTP, clientes HTTP, microserviços e WebSockets. Diferente do [Spring Boot](/blog/kotlin-spring-boot/), que traz um ecossistema gigante de convenções e auto-configuração, o Ktor segue a filosofia de ser **minimalista e modular**: você adiciona apenas o que precisa, sem mágica escondida.

As principais características do Ktor incluem:

- **Assincronicidade nativa**: construído sobre [coroutines](/blog/coroutines-kotlin/), cada requisição é uma coroutine — sem threads bloqueadas
- **DSL idiomático**: a configuração do servidor usa [DSLs de Kotlin](/blog/kotlin-dsl/), resultando em código expressivo e legível
- **Multiplataforma**: o cliente HTTP do Ktor roda em JVM, Native, JavaScript e WebAssembly
- **Modular**: sistema de plugins onde você instala só o que precisa (serialização, autenticação, CORS, etc.)
- **Transparente**: sem anotações mágicas, sem injeção de dependência implícita, sem proxy de classes

## Configurando o projeto

A maneira mais rápida de iniciar é pelo [Ktor Project Generator](https://start.ktor.io), onde você seleciona os plugins desejados e baixa o projeto pronto. Alternativamente, configure manualmente com Gradle:

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

dependencies {
    implementation("io.ktor:ktor-server-core")
    implementation("io.ktor:ktor-server-netty")
    implementation("io.ktor:ktor-server-content-negotiation")
    implementation("io.ktor:ktor-serialization-kotlinx-json")
    implementation("io.ktor:ktor-server-status-pages")
    implementation("ch.qos.logback:logback-classic:1.5.15")

    testImplementation("io.ktor:ktor-server-test-host")
    testImplementation("org.jetbrains.kotlin:kotlin-test")
}
```

O plugin `io.ktor.plugin` facilita o build e gera o fat JAR para deploy automaticamente.

## Hello World com Ktor

Vamos criar o servidor mais simples possível:

```kotlin
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                call.respondText("Olá, Ktor!")
            }
        }
    }.start(wait = true)
}
```

Execute e acesse `http://localhost:8080` — pronto, seu servidor Ktor está rodando. Note como tudo é explícito: você vê o engine (Netty), a porta, as rotas. Nada acontece sem que você configure.

## Roteamento com a DSL do Ktor

O sistema de roteamento do Ktor usa uma DSL expressiva que suporta agrupamento, parâmetros de caminho e todos os métodos HTTP:

```kotlin
fun Application.configureRouting() {
    routing {
        route("/api/v1") {
            get("/tarefas") {
                // Listar todas as tarefas
            }

            get("/tarefas/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                    ?: return@get call.respondText(
                        "ID inválido",
                        status = HttpStatusCode.BadRequest
                    )
                // Buscar tarefa por ID
            }

            post("/tarefas") {
                // Criar nova tarefa
            }

            put("/tarefas/{id}") {
                // Atualizar tarefa existente
            }

            delete("/tarefas/{id}") {
                // Remover tarefa
            }
        }
    }
}
```

A função `route("/api/v1")` agrupa todas as rotas sob um prefixo comum, evitando repetição. Os parâmetros de caminho como `{id}` são acessados via `call.parameters`.

## Serialização JSON com kotlinx.serialization

Para trabalhar com JSON, o Ktor se integra nativamente com o `kotlinx.serialization`. Primeiro, instale o plugin de Content Negotiation:

```kotlin
fun Application.configureSerialization() {
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            isLenient = true
            ignoreUnknownKeys = true
        })
    }
}
```

Agora defina seus modelos de dados com a anotação `@Serializable`:

```kotlin
import kotlinx.serialization.Serializable

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

@Serializable
data class TarefaRequest(
    val titulo: String,
    val descricao: String = ""
)
```

## Exemplo completo: API CRUD de tarefas

Agora vamos juntar tudo em uma API CRUD funcional. Usaremos uma lista em memória para simplificar (em produção, você conectaria a um banco de dados com Exposed ou ktorm):

```kotlin
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.concurrent.atomic.AtomicInteger

val tarefas = mutableListOf<Tarefa>()
val idCounter = AtomicInteger(0)

fun Application.configureTarefasRoutes() {
    routing {
        route("/api/tarefas") {

            // GET — listar todas
            get {
                call.respond(tarefas)
            }

            // GET — buscar por ID
            get("/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val tarefa = tarefas.find { it.id == id }
                if (tarefa == null) {
                    call.respond(HttpStatusCode.NotFound, "Tarefa não encontrada")
                } else {
                    call.respond(tarefa)
                }
            }

            // POST — criar nova
            post {
                val request = call.receive<TarefaRequest>()
                val novaTarefa = Tarefa(
                    id = idCounter.incrementAndGet(),
                    titulo = request.titulo,
                    descricao = request.descricao
                )
                tarefas.add(novaTarefa)
                call.respond(HttpStatusCode.Created, novaTarefa)
            }

            // PUT — atualizar existente
            put("/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val index = tarefas.indexOfFirst { it.id == id }
                if (index == -1) {
                    call.respond(HttpStatusCode.NotFound, "Tarefa não encontrada")
                } else {
                    val request = call.receive<TarefaRequest>()
                    val atualizada = tarefas[index].copy(
                        titulo = request.titulo,
                        descricao = request.descricao
                    )
                    tarefas[index] = atualizada
                    call.respond(atualizada)
                }
            }

            // DELETE — remover
            delete("/{id}") {
                val id = call.parameters["id"]?.toIntOrNull()
                val removido = tarefas.removeAll { it.id == id }
                if (removido) {
                    call.respond(HttpStatusCode.NoContent)
                } else {
                    call.respond(HttpStatusCode.NotFound, "Tarefa não encontrada")
                }
            }
        }
    }
}
```

## Plugins essenciais

O Ktor funciona com um sistema de plugins que você instala conforme a necessidade. Além do ContentNegotiation que já vimos, os mais importantes são:

**StatusPages** — tratamento centralizado de erros:

```kotlin
install(StatusPages) {
    exception<IllegalArgumentException> { call, cause ->
        call.respond(HttpStatusCode.BadRequest, cause.localizedMessage)
    }
    exception<Throwable> { call, cause ->
        call.respond(
            HttpStatusCode.InternalServerError,
            "Erro interno: ${cause.localizedMessage}"
        )
    }
}
```

**CORS** — controle de acesso cross-origin:

```kotlin
install(CORS) {
    allowHost("meusite.com.br")
    allowHeader(HttpHeaders.ContentType)
    allowMethod(HttpMethod.Put)
    allowMethod(HttpMethod.Delete)
}
```

**CallLogging** — logs estruturados de cada requisição:

```kotlin
install(CallLogging) {
    level = Level.INFO
    filter { call -> call.request.path().startsWith("/api") }
}
```

Outros plugins populares incluem Authentication (JWT, OAuth, Session), WebSockets, Rate Limiting e Compression.

## Ktor vs Spring Boot: quando usar cada um?

Essa é uma das perguntas mais comuns entre devs Kotlin no backend. A resposta depende do contexto:

| Critério | Ktor | Spring Boot |
|---|---|---|
| **Filosofia** | Minimalista, explícito | Convenção sobre configuração |
| **Curva de aprendizado** | Mais simples para quem sabe Kotlin | Requer conhecer o ecossistema Spring |
| **Performance** | Mais leve, menos overhead | Mais pesado, mas altamente otimizado |
| **Ecossistema** | Menor, mas crescendo | Gigantesco, maduro |
| **Multiplataforma** | Sim (cliente HTTP) | Apenas JVM |
| **Ideal para** | Microserviços, APIs leves, serverless | Aplicações enterprise, monolitos |

Se você vem do mundo Java e já conhece Spring, a combinação [Kotlin + Spring Boot](/blog/kotlin-spring-boot/) oferece produtividade imediata. Se quer algo 100% Kotlin, sem heranças do mundo Java, o Ktor é o caminho natural. Para [deploy serverless na AWS Lambda](/blog/kotlin-aws-lambda/), o Ktor se destaca pelo cold start mais rápido.

## Deploy: fat JAR para produção

O plugin do Ktor gera um fat JAR automaticamente. Basta rodar:

```bash
./gradlew buildFatJar
```

O JAR fica em `build/libs/seu-projeto-all.jar`. Para executar em produção:

```bash
java -jar build/libs/seu-projeto-all.jar
```

Para containerizar com Docker, um Dockerfile simples funciona:

```dockerfile
FROM eclipse-temurin:21-jre-alpine
COPY build/libs/seu-projeto-all.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
```

O Ktor também suporta GraalVM Native Image para builds nativos com cold start de milissegundos — ideal para [ambientes Kubernetes](/blog/kotlin-kubernetes/) e serverless.

## Testando a API

Ktor tem um módulo de testes embutido que permite testar rotas sem subir o servidor de verdade:

```kotlin
@Test
fun `deve criar tarefa com sucesso`() = testApplication {
    application {
        configureSerialization()
        configureTarefasRoutes()
    }

    val response = client.post("/api/tarefas") {
        contentType(ContentType.Application.Json)
        setBody("""{"titulo": "Estudar Ktor", "descricao": "Completar tutorial"}""")
    }

    assertEquals(HttpStatusCode.Created, response.status)
}
```

O `testApplication` cria um ambiente de teste completo sem abrir portas de rede, tornando os testes rápidos e isolados.

## Conclusão

O Ktor é um framework poderoso que abraça a filosofia do Kotlin: conciso, seguro e explícito. Ele é perfeito para microserviços, APIs REST e aplicações onde você quer controle total sobre o que acontece. Combinado com [coroutines](/blog/coroutines-kotlin/) para concorrência e kotlinx.serialization para JSON, você tem um stack moderno e performático para o backend.

Se está começando com [Kotlin no servidor](/blog/kotlin-server-side-2026/), o Ktor é uma excelente porta de entrada — mais acessível que o Spring Boot e com uma experiência de desenvolvimento verdadeiramente Kotlin-first. Se quiser comparar com frameworks de outras linguagens, veja como <a href="https://golang.com.br/blog/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go lida com APIs usando Gin e Echo</a> ou como <a href="https://python.dev.br/blog/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python aborda o problema com FastAPI e Django</a>.
