15. October 2019
7 min

Create a REST Application with kTor and Exposed

Create an asynchronous backend application with Ktor and Exposed using PostgreSQL

In this tutorial we will create a rest backend application with kTor and exposedkTor is a JVM Microframework for building asynchronous servers and clients in Kotlin. Exposed is a lightweight SQL library on top of JDBC driver for Kotlin language. With exposed it is possible to access databases in two flavours: typesafe SQL wrapping DSL and lightweight Data Access Objects (DAO). In this tutorial we will be using the DSL way to achieve our goals.

If you lose track with this tutorial you can find the example code here: https://github.com/mbagemihl/ktor-exposed-demo

To create a REST Application with kTor I recommend to first head to start.ktor.io and generate a Project with the necessary dependencies as follows:

  • Content Negotiation and either Jackson or GSON depends on you personal preferences, I will choose Jackson for this Tutorial.
  • Routing to create different Routes for different operations.

And that’s it for the moment. If you filter the Project Generator window for only marked dependencies it should look like this

ktor dependencies for simple backend application

kTor dependencies

Now choose a name, and the other preferences and create a project and open it in your favorite kotlin IDE. If you are using intelliJ you can use the kTor plugin as well.

After importing the created project into your IDE you can already start the app with gradle run (if you chose gradle to build your app) and a GET to localhost:8080 will already return a simple HELLO WORLD!

If we now have a look at the file Application.kt we can can see why we are getting this answer. It created for us 2 initial endpoints. But to have at least a CRUD functionality it needs to have a bit more than that.

Let’s assume we want to have some kind of simple User management and create a User data class.

As you can see my User has only 4 attributes which are all mandatory and a UUID will be generated by default. Now we need to make sure to persist our data in a database. For this I am using PostgreSQL. After setting up the database my table looks like this:

database table users

Implement database connection with Exposed

Before we can do anything with our database we need to import the dependencies for the Exposed Framework and the Hikari Connection Pool as well as PostgreSQL by including the following statements into the dependencies block in your build.gradle:

If you want to use the latest version go to https://github.com/JetBrains/Exposed.

Now we need to configure our connection Pool by adding a hikari.properties file to our resources path and define the following properties:

After this we need to initialize the connection pool in the application by creating a initDB function and calling it in the Application.module function:

If we now run the application again (and did not make any mistakes so far) we can see that the connection pool is initialized correctly because of logs similar to this:

Querying the database

Exposed offers a DSL to query a database with objects that inherit the Table class of Exposed in which we need to configure the names and types of the specific table. In my case the object for my Users table looks like this:

When the Table object is defined we are able to call methods on it to call our database.

Getting all Users

For that we define a Controller that handles all our transactions on the database and create a first getAll() method. We make the method return an ArrayList<User> so we need to declare and initialize this list before we make the transaction and map the result of it to user objects we add to this list.

If we now call getAll() it will get all users of our Users Table in the database and return the entries as an ArrayList. All we need to get use of that is instantiate the userController, create an Endpoint to GET /Users and return the result of this method.

But at this point we don’t have any Users in our List.

Adding a User

Let’s change that by adding the transaction to our UserController an implementing a POST /Users endpoint.

As you can see we are creating a random UUID when inserting the new User to the Database. This is necessary because we do not want to provide a new UUID in our request everytime we want to add a user. But now we either need an extra UserDTO class or we make the id optional. Since the id is our identificator it would be bad practice to make it optional. So we are creating a UserDTO data class to make sure we don’t need to send a UUID with our requests. Our two data classes should look like this now:

Now we can change the signature of our insert Method to the following:

After that it is time to create the POST /users endpoint and connect it to our repository.

As you can see we simply define we want to receive a UserDTO object which we just pipe through to our userController and return HTTP Code 201 if the transaction was successful (if not the app will throw an Exception and return HTTP Code 500).

Now it’s time to test what we did so far. Let’s gradle run the application again and call our new endpoints. If we initially go to http://localhost:8080 we should see a blank page and our app should log something similar to the following:

If we see an exception instead we might have a problem with our database connection. Now let’s insert some users into our list so we can actually see them when calling that endpoint.

Let’s set up a Request to POST /users with a user provided in the body like this

If this Request was successful the app returns Code 201 and if we now do a GET /users we will receive an answer similar to the following

And that’s it! We’re now able to add Users to our list and get that userlist afterwards. Almost! Since we decided to save the age of the user instead of the birthdate (which would be a way better pattern!) we will at least need a way to change that age!

Changing user data

So let’s create a PUT /users/{id} endpoint to change details of a user. As you may already expect we first need to add the transaction method to our UserController.

Note that the condition which user to update is provided as a parameter in the update block. With this transaction every attribute of the user will be updated whether it changed or not.

Let’s create the endpoint to PUT /users/{id}.

Similar to the POST we receive a UserDTO but this time we also get a user id as path variable. We just need to parse the string to a UUID and call our newly created update method. If no Exception is thrown the app responds with an HTTP 200.

Let’s see what happens if we now do a PUT to /users/b7e1e287-7969-4c4f-b60b-88d2c4d3a6b6 (UUID of our only registered user) with the following JSON body:

This call should change the age of user Marcel to 32 (Happy Birthday to me!). If we did nothing wrong a GET /users should now return the updated user in the list.

So now we are able to add and change users and get a list of all users. Now we need to make sure we are able to remove a user from our list.

Delete a user

Again we create the UserController function first.

And connect it to a newly created DELETE /Users/{id} Endpoint.

If we send a DELETE to /users/b7e1e287-7969-4c4f-b60b-88d2c4d3a6b6 the app should return StatusCode 200 and afterwards a GetAll should return an empty array as if we never added a user in this Tutorial.