Ktor framework is a powerful framework for building asynchronous servers and clients in Kotlin. It’s designed to be simple, efficient, and highly customizable, making it a popular choice for building modern web applications, microservices, and APIs. In this blog, we will cover both the Ktor server and Ktor client frameworks, providing code examples with detailed explanations to help you get started.
Why Choose Ktor Framework?
Ktor is a Kotlin-native framework that offers:
- Asynchronous: Built on coroutines for non-blocking I/O.
- Lightweight: Minimal overhead with customizable features.
- Highly Modular: You can choose only the components you need.
- Multiplatform: Supports both server-side and client-side development.
Setting Up Ktor Framework
Prerequisites
Before we dive into Ktor, ensure that you have:
- IntelliJ IDEA (recommended for Kotlin projects)
- JDK 8+ installed
- Gradle or Maven for dependency management
Adding Ktor to Your Project
Start by adding the Ktor dependencies in your build.gradle.kts
file:
dependencies {
implementation("io.ktor:ktor-server-core:2.3.0")
implementation("io.ktor:ktor-server-netty:2.3.0")
implementation("io.ktor:ktor-client-core:2.3.0")
implementation("io.ktor:ktor-client-cio:2.3.0")
implementation("io.ktor:ktor-client-serialization:2.3.0")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.0")
}
Ktor Server
Creating a Basic Ktor Server
Let’s create a basic Ktor server using the Netty engine. The server will listen for HTTP requests and respond with a message.
Step-by-Step Implementation
First, create a main
function to configure and start the Ktor server:
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
fun main() {
embeddedServer(Netty, port = 8080) {
configureRouting()
}.start(wait = true)
}
fun Application.configureRouting() {
routing {
get("/") {
call.respondText("Hello, Ktor Server!")
}
}
}
Here,
- embeddedServer: Starts a server using the Netty engine on port 8080.
- routing: Defines the routes for handling HTTP requests.
- respondText: Sends a plain text response to the client.
When you run this, visiting http://localhost:8080/
in your browser will display “Hello, Ktor Server!”
Adding JSON Serialization
Let’s extend the basic server by adding JSON support. We’ll respond with a JSON object instead of plain text.
Adding the Dependencies
dependencies {
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.0")
}
Extending the Server Code
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
@Serializable
data class User(val id: Int, val name: String)
fun Application.configureRouting() {
install(ContentNegotiation) {
json()
}
routing {
get("/user") {
val user = User(1, "amol pawar")
call.respond(user)
}
}
}
Here in above code,
- @Serializable: This annotation marks the
User
data class for JSON serialization. - ContentNegotiation: This plugin allows Ktor to automatically serialize Kotlin objects into JSON.
Now, when you visit http://localhost:8080/user
, you’ll get a JSON response like this:
{
"id": 1,
"name": "amol pawar"
}
Ktor Client
Ktor also provides a client library to make HTTP requests. Let’s create a client to make a GET request to the server we just created.
Creating a Basic Ktor Client
Step-by-Step Implementation
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
suspend fun main() {
val client = HttpClient(CIO)
val response: HttpResponse = client.get("http://localhost:8080/user")
println(response.bodyAsText())
client.close()
}
Here,
- HttpClient(CIO): Creates a Ktor client using the CIO (Coroutine-based I/O) engine.
- client.get: Makes an HTTP GET request to the server.
- bodyAsText(): Retrieves the response body as a string.
This client will send a request to http://localhost:8080/user
and print the JSON response in the console.
Handling JSON Responses
Let’s improve the client by automatically deserializing the JSON response into a Kotlin object.
Adding the Dependencies
Ensure the following dependencies are added for JSON deserialization:
implementation("io.ktor:ktor-client-serialization:2.3.0")
Modifying the Client Code
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.Serializable
@Serializable
data class User(val id: Int, val name: String)
suspend fun main() {
val client = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}
val user: User = client.get("http://localhost:8080/user")
println("User: ${user.name}")
client.close()
}
Here,
- json(): Installs the JSON plugin for the Ktor client, allowing automatic deserialization.
- val user: User: The GET request is now deserialized into a
User
object.
The client will fetch the user data from the server, deserialize it into a User
object, and print the user’s name.
Advanced Ktor Server Features
Adding Authentication
Ktor supports authentication out of the box. You can easily add basic or token-based authentication.
Step-by-Step Implementation for Basic Authentication
import io.ktor.server.auth.*
import io.ktor.server.plugins.*
fun Application.configureSecurity() {
install(Authentication) {
basic("auth-basic") {
validate { credentials ->
if (credentials.name == "admin" && credentials.password == "password") {
UserIdPrincipal(credentials.name)
} else null
}
}
}
routing {
authenticate("auth-basic") {
get("/secure") {
call.respondText("Authenticated!")
}
}
}
}
Here,
- install(Authentication): Enables basic authentication.
- validate: Checks the provided credentials.
- authenticate: Protects the
/secure
route with authentication.
Now, trying to access /secure
will prompt for a username and password.
Testing Your Ktor Application
Ktor provides built-in support for testing with TestApplicationEngine
. Here’s how to write a simple test case for your application:
import io.ktor.server.testing.*
import kotlin.test.*
class ApplicationTest {
@Test
fun testRoot() = testApplication {
client.get("/").apply {
assertEquals("Hello, Ktor Server!", bodyAsText())
}
}
}
Here,
- testApplication: Creates a test environment for your Ktor server.
- client.get: Sends an HTTP GET request to the server.
- assertEquals: Asserts that the response matches the expected output.
Conclusion
Ktor is a powerful and flexible framework that allows you to build both server-side and client-side applications in Kotlin. Its modularity, asynchronous capabilities, and easy integration with Kotlin’s coroutines make it ideal for modern web development. Whether you’re building APIs, microservices, or full-stack applications, Ktor provides the tools you need.
By following the steps above, you should have a solid foundation in both Ktor server and client development. The examples here are just the beginning; you can explore more advanced features such as WebSockets, sessions, and caching as you continue to work with Ktor.
Happy coding!