DEV Community

Cover image for Building Blocks: A Beginners Guide to REST APIs with Kotlin and Ktor
Audu Ephraim
Audu Ephraim

Posted on

Building Blocks: A Beginners Guide to REST APIs with Kotlin and Ktor

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:

Image description

  1. Give a project name of your choice, In this article, we will name our project "SampleKtorProject"
  2. 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.
  3. 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
)
Enter fullscreen mode Exit fullscreen mode

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)
       }
   }
}
Enter fullscreen mode Exit fullscreen mode

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()
   }
}
Enter fullscreen mode Exit fullscreen mode

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:

Image description

  • 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:

You should see a response like this:

Image description

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:

Image description

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

Image description

  • Click send, and you will see a response “country Added”

Image description

  • Change the request type to get and send the request, you should see the newly added country

Image description

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)