In the world of software development, Rest APIs have emerged as fundamental building blocks of modern web applications.
If you're a novice or an experienced developer, looking to experience new territories, this guide is your starting point for understanding and creating REST APIs using Kotlin and Ktor.
Kotlin is a modern language that offers more concise syntax than Java. ktor is a Kotlin framework for building asynchronous servers and clients are gaining a lot of traction in the tech industry.
This powerful duo simplifies building lightweight, scalable, and efficient REST APIs.
In this guide, we’ll create a basic REST API. This API will return a list of countries and their details. It will also allow us to view information for a specific country and add a new country with its details.
Our goal is to simplify the concept of REST APIs and provide a clear, step-by-step method to build one using Kotlin and Ktor. So, let’s get started!
Prerequisites
Basic knowledge of Kotlin and basic HTTP Knowledge
What is a REST API: A Brief Introduction
An API(Application Programming Interface) is a set of rules and protocols that define how applications, devices, and services can communicate with themselves over the Internet using different types of requests to get different types of information.
A REST(REpresentation Stateful Transfer) API is an API that conforms to the design principles of the REST architectural style. You can read more about this architecture and its principles HERE.
KTOR
As we earlier said, Ktor is a Kotlin framework for building asynchronous server-side and client-side applications with ease. It is entirely written in Kotlin and supports coroutines for asynchronous programming.
Setting Up Ktor and Kotlin
To start working with Ktor and Kotlin we need to set up our development environment.
To do that we will be making use of IntelliJ IDEA which is the leading Kotlin and Java IDE you can get it Here. You can download the ultimate edition which is paid or you can just get the community edition which I will be using. After downloading just follow the prompts to install.
To get started with Ktor, we need to head over to the Ktor project generator, if you are using the Ultimate Edition of IntelliJ you will be able to create your Ktor project right from inside your IDE.
In the Ktor project generator, you should see a page like this:
- Give a project name of your choice, In this article, we will name our project "SampleKtorProject"
- Click on Adjust Project Settings, and you will see a drop-down with a bunch of options that should help you customize your project even more. But for the sake of this guide, we will leave everything as it is.
- Then click on Add plugins These plugins allow us to have access to specific back-end behaviors that we typically use in backend frameworks. If you scroll down the list, you should see all available plugins, but for this project, you will be adding just four plugins.
Routing- allows us to define the routes we respond with data. In our case countries
ContentNegotiation- used for JSON parsing. When we respond with a Kotlin data class it will automatically be parsed to a JSON
CallLogging- this will just log when a client requests a server so we can see what's happening.Kotlinx.serialization- which is used by the content negotiation for serializing and deserializing the data
When we are done you can go ahead and click on generate project, which automatically downloads the project in a zip file. Unzip and save in any location of your choice, then open the project in IntelliJ. Wait for the project to finish building.
Let’s go over to the project
Project Structure
We have the build.gradle.kts file. It is the Kotlin version of Gradle. Gradle is a build automation tool, if you are coming from an Android background this should be familiar. It contains our project dependencies and some project settings.
Navigate to src->main-> Kotlin in which we have our Application.kt file which is the main entry point of our program. Similar to mainActivity in Android. In this file, we have our main function as we know from plain kotlin.
head over to the plugins folder, here you will see all the plugins we added in our initial project setup, which created some default files
In the call Monitoring.kt, you’ll see a lambda block where you can customise your logging. Like how detailed you want your logs to be and if you want to filter your log messages.
In the Routing.kt file this is where the routes are defined, that our server responds at. Here we define and configure our routes.
To test that everything is working perfectly, head over to Application.kt, and click on the run button beside fun main(){...} to start the project, head over to any browser of your choice and hit the address returned in your IntelliJ console.
This would return “hello world” as seen in your routing file. Now that we are familiar with our project, let’s start building.
Since this is a very simple project we won't go deep into structuring our project.
Let us create two new packages in our root folder, Let's call them “model” and “routes” To do this:
right-click on your root package->new->package
-
Inside the model package let's create a new data class and call it “country”. In our API each country has an id, a name, a continent, and a capital. The data class will end up looking like this:
@Serializable data class Country( val id: String, val name: String, val continent: String, val capital: String )
The “@Serializable” annotation enables us to parse this content of data class content to JSON and send it over the network.
- Next inside the route package create a new file, named countriesRoute.kt, and add the following code.
private val countries = mutableListOf<Country>(
Country("1","Japan", "Asia", "Tokyo"),
Country("2","Egypt", "Africa", "Cairo"),
Country("3","Canada", "North America", "Ottawa"),
Country("4","Brazil", "South America", "Brasilia"),
Country("5","New Zealand", "Oceania", "Wellington"),
Country("6","England", "Europe", "London"),
)
fun Route.countries(){
route("/countries"){
get{
call.respond(
HttpStatusCode.OK,
countries
)
}
get("{id?}"){
val id = call.parameters["id"]?: return@get call.respondText(
"missing id",
status = HttpStatusCode.BadRequest
)
val country = countries.find { it.id == id }?:return@get call.respondText(
"country with id $id not found",
status = HttpStatusCode.NotFound
)
call.respond(country)
}
post{
val country = call.receive<Country>()
countries.add(country)
call.respondText("country Added ", status = HttpStatusCode.Created)
}
}
}
Let’s go over it:
The data that we return to a client upon a request does not originate from a database, we create a mutable list of Country objects. Each country object has an id, name, continent, and capital.
fun Route.countries()
: It is an extension function of the route class in kotlin. It is a way to group related routes.route("/countries")
: It sets up a route group called countries with the path/countries
. All routes defined within the block will have/countries
as their base path.GET: It sets up a get request. When a get request is made to
/countries
, the server responds with a status code of ok and a list of countries.get("{id?}")
: This sets up another get request, but this one expects an optional id parameter in the URL. If an ID is provided e.g.countries/1
. It tries to find a country with that id. If it finds one it responds with the country data; otherwise, it responds with a not found status and the message “country with id not found”. If no ID is provided for example:/countries/
it responds with a bad request status code.Post: this sets up a POST request. It expects the request to contain a country object. It adds the new country to the countries list and responds with a created status and a confirmation message
Registering routes
Now all we have to do is head on to our plugin package in the Routing.kt file, remove the default, hello world get request, and call countries()
like this:
fun Application.configureRouting() {
routing {
countries()
}
}
Testing with Postman
Our API is ready for testing. We are going to use Postman to see how it works.
Postman is a tool for testing API endpoints and seeing how it works. You can get it here.
Once you’ve downloaded, installed, and launched Postman, you should see a window like this:
click on the + to open a new tab.
After that, head back to IntelliJ, and launch your server. Once it is up and running without any errors, return to Postman.
Testing GET requests
First, let's test out GET to return the list of every country and details about them. To do this:
Select GET
In the empty text field, put the address we want to request, in our case, it is http://127.0.0.1:8080/countries then hit send.
You should see a response like this:
Great!! Our get request is working properly.
Let's try and get a country with ID 3. Edit the URL, to look like this http://127.0.0.1:8080/countries/3 and hit send.
You should get a response like this:
Testing Post request
Change the request type to POST
Edit the URL to http://127.0.0.1:8080/countries
Select “body” and “raw” in the options below the URL text field
Enter the data of the country you want to add
- Click send, and you will see a response “country Added”
- Change the request type to get and send the request, you should see the newly added country
Our API is now working and it's returning the desired results.
Conclusion
In this article, we've taken our first steps into the world of API development with Kotlin and Ktor. We've seen how Kotlin's concise syntax and Ktor's robust features can make the process of building APIs more efficient and enjoyable.
We started with the basics of setting up a Ktor project and gradually moved on to creating our first API endpoints with Ktor. We've also learned about key concepts such as routing and request handling.
However, this is just the beginning. There's much more to explore in the realm of Kotlin and Ktor, such as authentication, database integration, and testing. As you continue your journey, remember that the key to mastering any technology is consistent practice and curiosity.
So, keep building, keep exploring, and most importantly, have fun doing it.
Happy coding!
Top comments (0)