Ktor is a lightweight framework written in Kotlin programming language. This framework allows us to create asynchronous servers and clients. In this article, I will explain the basics of Ktor framework by creating a simple REST service.
To start, You’ll need IntelliJ IDEA with the Ktor plugin installed. IntelliJ is an IDE developed by JetBrains. You can download IntelliJ IDEA from jetbrains.com/idea/.
To install the Ktor plugin, start IntelliJ and select Help ▸ Settings on Windows or IntelliJ menu ▸ Preferences on Mac.
Then select the Plugins section and search for Ktor. Install the plugin and restart IntelliJ.
Starting a project in IntelliJ
To start a new project in IntelliJ Go to New ▸ Project
The project type has to be ktor, if ktor is not on the list, restart IntelliJ, also make sure Gradle is the Project type and Netty for server Type.
Next, select your features. Under Features under the server section, choose Locations, Sessions and Routing. Locations and Routing handle API routes. Sessions keep track of the current user so you have a state associated with it.
For Authentication, choose Authentication JWT – JSON Web Token – to create JSON-based access tokens. For Content Negotiation, choose GSON, a library used to serialize and deserialize Java objects to and from JSON.
Select Next and on the next screen, set the GroupId to com.example and the ArtifactId to employee Then select Next. we’ll be creating a server to create new employees in a company
Click Next to and set the name of the project to Employee and set project location, then click Finish
to allow project creation. After the project has been created, go to Build ▸ Build Project and ensure your project builds without errors.
Click on the pink hammer to ADD CONFIGURATION to your project at the top right corner of the IDE, this will enable us to run the boilerplate code generated in the Application.Kt file when the project was created “Hello World“.
Click on the add button(+), pick kotlin and under Use classpath of module and set to employee.main and under Main class choose the com.example.Application.kt and click Ok
Before running the app, the application.conf can be adjusted to the preferred port and host address, it is advisable to change from 8080 and 0.0.0.0 which is the default port and host respectively because the 8080 port is always most likely in use by another service on the computer
The port was changed to 5000 and the host was changed to a custom one 127.0.0.1 and run the App, you should see Responding at http://127.0.0.1:5000. Click on the link and you should see “hello world“ on the webpage.
Remove everything in routing section, Type and MyLocation under Application.kt
Your next step is to implement the employee API
Here’s how the API for employees will look:
employees/create (POST): Creates a new employee. Passes in the email address, name and password.
employees/login (POST): Logs in an employee using the email address and password
Define the Routes
In Ktor, a Route defines a path to your server. This could be an API, like the one you’re creating, or your home page. For example
This is a GET that responds to the string path “/” (i.e., the root path) with the text “HELLO WORLD!”
Setting up Postgres
On Mac, download the Postgres app from postgresapp.com.
Also, you can download pgAdmin from https://www.pgadmin.org/download/, this can serve as an Admin GUI for managing servers/databases
Once you’ve installed Postgres, create a database, which you’ll name employees for this project. To create it, run Postgres from the command line with:
Once in Postgres, create the database by typing:
Note that you need the semi-colon at the end.
To connect to the database, type:
The tables do not need to be created, creation of tables will be handled with code
To access the database you’ve just created, you’ll add all the libraries the project needs:
- Exposed: A JetBrains library you use to easily access a database.
- Hikari: Use this library to set up the configuration for the Postgres database.
- postgresql: Provides the JDBC driver, which allows the code to interact with the database.
Open gradle.properties and add the following variables:
open build.gradle and add the following dependencies
In IntelliJ, open up the Gradle tab and choose sync.
We’ll be using environment variables System.getEnv() to connect safely to our database
click COM.EXAMPLE.APPLICATION.KT then click the edit configuration to edit the current configuration
Under the Environment Variables section, click the button on the far right and add the following parameters:
Here’s what these parameters are doing:
JDBC_DRIVER: Sets the driver for Postgres.
JDBC_DATABASE_URL: The database connection URL.
SECRET_KEY: Use this for hashing.
JWT_SECRET: You’ll use this later for authentication.
The Environment Variable screen should look like this:
Be sure there are no extra spaces around the keys or values then click OK, then click OK to save configurations. Now click the green run button in the toolbar to build and run. There should be no errors.
Adding a Data Layer
The data layer provides a transparent layer that interacts with the underlying data store — Postgres, in our case. To achieve this, you’ll use the repository pattern.
In the following code, you’ll learn how to create the data models and classes that describe the tables for storing data. You’ll also learn how to implement a repository interface.
Setting up Model Classes
Before you can hook up the database, you need model classes. This project requires an Employee data class.
Create a file called Employee under the model folder and add the following:
This creates an Employee class with an email and display name. You’ll store the password as a hash value to protect it should the database be compromised.
Create a repository package/folder and under that create an Employees object, we will be using this object to create our database table
The table comes from the Exposed library. You can use the Column class or helpers like varchar to define the fields in the table. autoIncrement automatically creates IDs for new entries.
Create a file named DatabaseFactory to contain the class for connecting to the database.
The database is from the Exposed library. It allows you to connect to the database with HikariDataSource, which your Hikari method creates.
You use a transaction to create your Employees table. It will only create the table if they don’t already exist.
The hikari method is set up as below:
Steps 1 through 4 in the code above use the Environment Variables that you defined earlier.
Step 5 declares a helper function to wrap a database call in a transaction and have it run on an IO thread. This function uses Coroutines.
In this section, you’ll work on adding a repository interface to the project. The interface will wrap all calls to the database. Create a new file in the repository folder named Repository
This creates an interface that includes functions for adding and finding Users by ID and email. This is the entry point to interact with the data layer.
Now, implement this interface by creating a file named EmployeeRepository in the repository folder. Add the following:
Start with addEmployee by replacing its body with the following. This function will return an employee if one is successfully created.
- InsertStatement: An Exposed class that helps with inserting data.
- dbQuery: A helper function, defined earlier, that inserts a new Employee record.
- Uses the insert method from the Employees parent class to insert a new record.
- rowToUser: A private function required to convert the Exposed ResultRow to your Employee class.
Now, you need to add the definition of rowToUser:
For the two other methods in the EmployeeRepository which are findEmployee and findEmployeeByEmai
Run a Build ▸ Build Project to ensure your project builds without errors.
You need to authenticate users to keep your server secure. When you created your server, you chose the JWT authentication feature. But you’ll need to create a few functions to use it.
create an auth package/folder then create an Auth.kt file under it then include the following:
- Makes use of the SECRET_KEY Environment Variable defined in step Use this value as the argument of the hex function, which turns the HEX key into a ByteArray. Note the use of @KtorExperimentalAPI to avoid warnings associated with the experimental status of the hex function.
- Defines Environment Variable.
- Creates a secret key using the given algorithm, HmacSHA1.
- hash converts a password to a string hash.
Next, create a new class in auth named JwtService and add the following:
The previous code requires the JWT_SECRET Environment Variable in step 1 to create the JWTVerifier in step 2. generateToken, defined in step 3, generates a token that the API uses to authenticate the request. You’ll need this function later.
Next, create a file named MySession in the auth folder and add this code:
This stores the current userId in a Session.
In this section, you’ll configure the server. To do so, you’ll use some of the methods you created earlier.
Open Application.kt, remove MySession and import the newer MySession you just created.
Add the following after the install(Sessions) section:
The first part of this code initializes the data layer you defined earlier, while the second part handles authentication.
Next, add the following to the install(Authentication) section:
You define the JWT name in step 1, which can be anything you want.
Step 2 specifies the verifier you created in the JwtService class.
Step 3 creates a method that runs each time the app needs to authenticate a call.
Finally, step 4 tries to find the user in the database with the userId from claimString. If the userID exists, it verifies the user. Otherwise, it returns a null user and rejects the route.
Before you continue, run your server to make sure everything compiles and runs properly. You should see several Hikari debug statements in the output window.
Adding the Employee Create Route
Create a Route.kt file
These are the imports you’ll need for the Route file
Then add the route constants we will be using
These define the strings needed for the routes, allowing you to create or log in an employee
Add the following route classes:
@Location(USER_LOGIN) associates the string USER_LOGIN with the UserLoginRoute class.
Then add the employee create a route:
Here’s how all that breaks down.
- Defines an extension function to Route named users that takes in a Repository, a JWTService and a hash function.
- Generates a route for creating a new user.
- Uses the call parameter to get the parameters passed in with the request.
- Looks for the password parameter and returns an error if it doesn’t exist.
- Produces a hash string from the password.
- Adds a new employee to the database.
Add the following code to the routing section in Application.kt:
Perform a Build ▸ Build Project to ensure your project still builds without errors
Then Run the server
The Postman app is a terrific tool for testing APIs. Find it at getpostman.com/.
Once installed, open Postman and use a new tab to make a POST request to generate a new user. Use localhost:5000/employees/create. In the Body tab, add these three variables: displayName, email and password.
Use any data you want in the Value column. Press Send and you’ll get a token back.
To save that token in Postman, set a global variable that you can use in other calls. To do so, go to the Tests tab and add:
Customize Response from Server
We will have to add files to models namely the payload and SignUpResponse under the models package:
Then replace the code below under the Routes
Then rerun the server and fill in the appropriate details on postman:
You get the details of the just created user/employee in a JSON format
This service listens for only POST requests and performs create database operation with Postgres, you can perform other requests such as GET, PUT and DELETE requests and perform CRUD database operations with Postgres
If you want to learn more about APIs, visit these links
- How APIs work — An Analogy For Dummies https://medium.com/@tyteen4a03/how-apis-work-an-analogy-for-dummies-ac6ee1d1671b
- How do APIs work? https://tray.io/blog/how-do-apis-work
if you want to see more of ktor support visit ktor.io/