<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ktor Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/ktor/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:50:21 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>Ktor Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Secure REST API with Ktor &#8211; Role-based access control (RBAC)</title>
		<link>https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/</link>
					<comments>https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 19 Dec 2023 06:00:00 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[RBAC]]></category>
		<category><![CDATA[REST]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9008380</guid>

					<description><![CDATA[<p>In this, 3rd article in a series, I will show you how to add role-based access control (RBAC / role-based security) to our Ktor project.</p>
<p>The post <a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/">Secure REST API with Ktor &#8211; Role-based access control (RBAC)</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Hello and welcome to the 3rd article in our secure REST API with Ktor series, in which I will show you how to set up <strong>role-based access control (RBAC)</strong>, also known as role-based security. </p>



<p>If you haven&#8217;t seen my previous articles, then I highly encourage you to check them out: </p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/" target="_blank" rel="noreferrer noopener">Secure REST API with Ktor and JWT Access Tokens</a></li>



<li><a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/" target="_blank" rel="noreferrer noopener">Secure Ktor app with JWT refresh tokens. Refresh token flow</a>.</li>
</ul>



<p>Moreover, in this tutorial, we will build functionality based on the refresh token article, so I highly encourage you to fetch the code from <a href="https://github.com/codersee-blog/kotlin-ktor-jwt-access-refresh-tokens" target="_blank" rel="noreferrer noopener">this repo</a> now. <strong>But don&#8217;t worry</strong>, if you are interested only in the RBAC part, then please navigate to the &#8220;<em>Custom Ktor Authorization Plugin</em>&#8221; paragraph.</p>



<h2 class="wp-block-heading" id="h-video-tutorial">Video Tutorial</h2>



<p>If you prefer <strong>video content</strong>, then check out my video:</p>



<div style="text-align: center; width: 90%; margin-left: 5%;">
<a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FgvhXfzl34pk%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p></div>



<p>If you find this content useful,<strong> please leave a subscription&nbsp;</strong> 😉</p>



<h2 class="wp-block-heading" id="h-what-exactly-will-we-do-today">What Exactly Will We Do Today? </h2>



<p>At the end of this tutorial, you will know precisely how to secure your Ktor REST API with role-based access control (RBAC). </p>



<p>But to be more specific, we will work with two endpoints: </p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="648" src="http://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_what_will_we_implement-1024x648.png" alt="Image shows two endpoint - get all users and get user by ID which will be secured with RBAC" class="wp-image-9008393" srcset="https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_what_will_we_implement-1024x648.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_what_will_we_implement-300x190.png 300w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_what_will_we_implement-768x486.png 768w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_what_will_we_implement.png 1252w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The <code>GET /api/user/{id}</code> will be accessible for users with either <code>ADMIN</code> or <code>USER</code> roles. The second one, <code>GET /api/user</code>, will be accessible only for <code>ADMIN</code> users. </p>



<p>And how we will achieve that? </p>



<p>Well, we will implement a <strong>custom Ktor authorization plugin</strong>, which will be triggered on the <strong>AuthenticationChecked hook</strong>. </p>



<p>And I&#8217;ll show you how to do it the easy way, without unnecessary logic 🙂 </p>



<h2 class="wp-block-heading" id="h-what-is-role-based-access-control-rbac">What Is Role-Based Access Control (RBAC)? </h2>



<p>Again, before we learn how to implement role-based access control (RBAC) in Ktor, let&#8217;s learn a bit about it. </p>



<p><strong>Role-based access control</strong> (RBAC) or role-based security is nothing else than a way of restricting system access to authorized users. </p>



<p>To put it simply, we define a set of <strong>roles</strong> in our system, which reflects the needs of given permissions from our organization. An example of such a role can be a <em>user, manager, admin</em>, and many many more. And based on this role we allow, or deny access for a particular, authenticated user. </p>



<p>When dealing with JWT tokens in a REST API, such information can be first stored inside the token, so that later, we can easily read a claim and check whether a request should be served, or not. </p>



<h2 class="wp-block-heading" id="h-update-user-class-and-repo">Update User Class and Repo</h2>



<p>With all of that said, let&#8217;s finally start the practice part. </p>



<p>As the first step, let&#8217;s navigate to the <code>User</code> class and insert a new field- <code>role</code>:  </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import java.util.UUID

data class User(
  val id: UUID,
  val username: String,
  val password: String,
  val role: String
)</pre>



<p>For the sake of simplicity, let&#8217;s leave it as a String value, but depending on your needs, you may want to consider using an <strong>enum</strong>, or even a <strong>sealed class</strong> instead.</p>



<p>At this point, our code won&#8217;t compile, so let&#8217;s navigate to the <code>UserRoute.kt</code> and update the <code>.toModel()</code> extension function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">private fun UserRequest.toModel(): User =
  User(
    id = UUID.randomUUID(),
    username = this.username,
    password = this.password,
    role = "USER"
  )</pre>



<p>As we can see, by default, all created users will have the <code>USER</code> role assigned. </p>



<p>Lastly, let&#8217;s navigate to the <code>UserRepository</code> and add some <code>ADMIN</code> user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class UserRepository {

  private val users = mutableListOf(
    User(UUID.randomUUID(), "admin", "password", "ADMIN")
  )

  // the rest of the class
}</pre>



<h2 class="wp-block-heading" id="h-add-role-claim-to-jwt-token">Add Role Claim to JWT Token</h2>



<p>As the next step, let&#8217;s open up the <code>JwtService</code> and update these 3 functions: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createAccessToken(username: String, role: String): String =
  createJwtToken(username, role, 3_600_000)

fun createRefreshToken(username: String, role: String): String =
  createJwtToken(username, role, 86_400_000)

private fun createJwtToken(
  username: String,
  role: String, 
  expireIn: Int,
): String =
  JWT.create()
    .withAudience(audience)
    .withIssuer(issuer)
    .withClaim("username", username)
    .withClaim("role", role)
    .withExpiresAt(Date(System.currentTimeMillis() + expireIn))
    .sign(Algorithm.HMAC256(secret))</pre>



<p>As I mentioned in the &#8220;theory&#8221; part- when dealing with JWT tokens, we can put information about the user&#8217;s role inside the token. </p>



<p>And that&#8217;s exactly what is happening here. From now on, every access and refresh token will contain an additional claim- the <code>role</code>&#8211; which will be populated with a role assigned to a user. </p>



<h2 class="wp-block-heading" id="h-update-userservice">Update UserService </h2>



<p>With that done, we must navigate to the <code>UserService</code> and update <code>authenticate</code> and <code>refreshToken</code> functions: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun authenticate(loginRequest: LoginRequest): AuthResponse? {
  val username = loginRequest.username
  val foundUser: User? = userRepository.findByUsername(username)

  return if (foundUser != null &amp;&amp; loginRequest.password == foundUser.password) {
    val accessToken = jwtService.createAccessToken(username, foundUser.role)
    val refreshToken = jwtService.createRefreshToken(username, foundUser.role)

    refreshTokenRepository.save(refreshToken, username)

    AuthResponse(
      accessToken = accessToken,
      refreshToken = refreshToken,
    )
  } else
    null
}

fun refreshToken(token: String): String? {
  val decodedRefreshToken = verifyRefreshToken(token)
  val persistedUsername = refreshTokenRepository.findUsernameByToken(token)

  return if (decodedRefreshToken != null &amp;&amp; persistedUsername != null) {
    val foundUser: User? = userRepository.findByUsername(persistedUsername)
    val usernameFromRefreshToken: String? = decodedRefreshToken.getClaim("username").asString()

    if (foundUser != null &amp;&amp; usernameFromRefreshToken == foundUser.username)
      jwtService.createAccessToken(persistedUsername, foundUser.role)
    else
      null
  } else
    null
}</pre>



<p>From now on we must pass the user role to the <code>JwtService</code> when creating new tokens and that&#8217;s exactly what we updated here. </p>



<h2 class="wp-block-heading" id="h-custom-ktor-authorization-plugin">Custom Ktor Authorization Plugin</h2>



<h3 class="wp-block-heading" id="h-what-are-ktor-plugins">What Are Ktor Plugins? </h3>



<p>Before we implement our custom one, let&#8217;s understand <strong>what exactly Ktor plugins are</strong>.</p>



<p>Basically, plugins in Ktor are a way to implement a common functionality that is out of the scope of the application logic. But what do we mean by that? Well, things like serialization, deserialization, compression, encoding, etc.</p>



<p>And when we take a look at the following diagram:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="612" src="http://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_plugins_diagram-1024x612.png" alt="" class="wp-image-9008395" srcset="https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_plugins_diagram-1024x612.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_plugins_diagram-300x179.png 300w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_plugins_diagram-768x459.png 768w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_rbac_plugins_diagram.png 1468w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>We will clearly see that plugins are everything that sits between request, application handler, and response. And yes, routing is a plugin too. </p>



<p>At this point, we can see that a custom plugin will be a great wait to achieve RBAC in our Ktor application. Moreover, <strong>we will use a new simplified API for creating custom plugins introduced in Ktor v2.0.0</strong>.</p>



<h3 class="wp-block-heading" id="h-implement-ktor-rbac-plugin">Implement Ktor RBAC Plugin</h3>



<p>With all of that said, let&#8217;s navigate to the <code>plugins</code> package and create the <code>RoleBasedAuthorization.kt</code> and write the following: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*
import io.ktor.server.response.*

class PluginConfiguration {
  var roles: Set&lt;String> = emptySet()
}

val RoleBasedAuthorizationPlugin = createRouteScopedPlugin(
  name = "RbacPlugin",
  createConfiguration = ::PluginConfiguration
) {
  val roles = pluginConfig.roles

  pluginConfig.apply {

    on(AuthenticationChecked) { call ->
      val tokenRole = getRoleFromToken(call)

      val authorized = roles.contains(tokenRole)

      if (!authorized) {
        println("User does not have any of the following roles: $roles")
        call.respond(HttpStatusCode.Forbidden)
      }
    }
  }
}

private fun getRoleFromToken(call: ApplicationCall): String? =
  call.principal&lt;JWTPrincipal>()
    ?.payload
    ?.getClaim("role")
    ?.asString()</pre>



<p>Firstly, we create the <code>PluginConfiguration</code> class with a <code>roles</code> field. This way, we will be able to configure later, which roles should be allowed for the given endpoint and which should be forbidden. </p>



<p>Later, we introduce our new, custom Ktor plugin using the <code>createRouteScopedPlugin</code> function. We must do that if we want to introduce a plugin that can be installed <strong>for a specific route</strong>. </p>



<p>Inside this function, we refer to  <code>pluginConfig</code> and configure what exactly our custom authorization plugin must do. Firstly, we set the <code>on</code> handler that accepts a <code>Hook</code> as a parameter- in our case the <code>AuthenticationChecked</code> is a hook. As per the <a href="https://ktor.io/docs/custom-plugins.html#other" target="_blank" rel="noreferrer noopener">documentation</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>AuthenticationChecked is executed after authentication credentials are checked. </cite></blockquote>



<p>Which simply means, that <strong>our plugin will be invoked after we authenticate the user successfully.</strong> With the old API, we would have to use the after <code>Authentication.ChallengePhase</code>.</p>



<p>Following, we <strong>extract the user role from the JWT <code>role</code> claim</strong> and if it is inside the Set with allowed roles, then we do nothing. Otherwise, we return <strong>403 Forbidden</strong>. </p>



<h2 class="wp-block-heading" id="h-add-roleutil">Add RoleUtil</h2>



<p>With that done, let&#8217;s navigate to the <code>util</code> package and introduce the <code>authorized</code> function inside the <code>RouteUtil.kt</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.plugins.RoleBasedAuthorizationPlugin
import io.ktor.server.application.*
import io.ktor.server.routing.*

fun Route.authorized(
  vararg hasAnyRole: String,
  build: Route.() -> Unit
) {
  install(RoleBasedAuthorizationPlugin) { roles = hasAnyRole.toSet() }
  build()
}</pre>



<p>To put it simply, we will use this function whenever we would like to <strong>authorize </strong>our request using role-based access control. </p>



<p>As a result, it will <strong>register our authorization plugin for a particular route.</strong></p>



<h2 class="wp-block-heading" id="h-update-user-routes">Update User Routes</h2>



<p>And with that said, let&#8217;s see it in action:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">authenticate {
  authorized("ADMIN") {

    get {
      val users = userService.findAll()

      call.respond(
        message = users.map(User::toResponse)
      )
    }

  }
}

authenticate("another-auth") {
  authorized("ADMIN", "USER") {

    get("/{id}") {
      val id: String = call.parameters["id"]
        ?: return@get call.respond(HttpStatusCode.BadRequest)

      val foundUser = userService.findById(id)
        ?: return@get call.respond(HttpStatusCode.NotFound)

      if (foundUser.username != extractPrincipalUsername(call))
        return@get call.respond(HttpStatusCode.NotFound)

      call.respond(
        message = foundUser.toResponse()
      )
    }
  }</pre>



<p>As we can see, from now on we can simply use the <code>authorized</code> whenever we want to limit endpoint accessibility to particular roles. </p>



<p>Of course, we must remember that this can be done only when the user is <strong>authenticated</strong>, because otherwise, we won&#8217;t be able to read the <code>role</code> claim. </p>



<h2 class="wp-block-heading" id="h-ktor-rbac-summary">Ktor RBAC summary</h2>



<p>And that&#8217;s all for this article on how to implement role-based access control (RBAC) in Ktor. </p>



<p>I hope this article (and a series) helped you to learn how easily we can set up different things related to security in Ktor. If you haven&#8217;t seen previous materials yet, then you can do that right here: </p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/">Secure REST API with Ktor and JWT Access Tokens</a></li>



<li><a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/">Secure Ktor app with JWT refresh tokens. Refresh token flow.</a></li>
</ul>



<p>As always, you can find a source code in <a href="https://github.com/codersee-blog/kotlin-ktor-custom-authorization-plugin-rbac-jwt/tree/main" target="_blank" rel="noreferrer noopener">this GitHub repository</a> and <strong>let me know your thoughts</strong> in the comments section below 🙂</p>
<p>The post <a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/">Secure REST API with Ktor &#8211; Role-based access control (RBAC)</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Secure Ktor app with JWT refresh tokens. Refresh token flow.</title>
		<link>https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/</link>
					<comments>https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 28 Nov 2023 06:00:00 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[JWT]]></category>
		<category><![CDATA[Resfresh Tokens]]></category>
		<category><![CDATA[REST]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9008372</guid>

					<description><![CDATA[<p>In the second article of my secure Ktor series, I will show you how to implement a JWT refresh token flow functionality.</p>
<p>The post <a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/">Secure Ktor app with JWT refresh tokens. Refresh token flow.</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In the second article of my secure <strong>Ktor </strong>series, I will show you how to implement a <strong>JWT refresh token flow</strong> functionality.</p>



<p>To not duplicate ourselves, we will use the project implemented in the previous blog post, in which we learned how to implement a secure <a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/" target="_blank" rel="noreferrer noopener">REST API with Ktor and authenticate with JWT access tokens</a>. So if you haven&#8217;t read that article yet, I highly encourage you to do it now. Nevertheless, if you are interested more in the <strong>refresh token part</strong>, you can find a link to the GitHub repository and simply clone the project. </p>



<p>Without any further ado, let&#8217;s get to work 🙂 </p>



<h2 class="wp-block-heading" id="h-video-tutorial">Video Tutorial</h2>



<p>If you prefer <strong>video content</strong>, then check out my video:</p>



<div style="text-align: center; width: 90%; margin-left: 5%;">
<a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2F97_u55vGQz0%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p></div>



<p>If you find this content useful,<strong> please leave a subscription&nbsp;</strong> 😉</p>



<h2 class="wp-block-heading" id="h-a-tiny-bit-of-theory">A Tiny Bit Of Theory</h2>



<p>If you&#8217;re following me for a longer period, you know that I focus on the practical side of different problems. </p>



<p>Nevertheless, I owe you a bit of explanation before we learn how to secure our Ktor app with JWT refresh token flow. So, in the following paragraphs, I will shortly describe: </p>



<ul class="wp-block-list">
<li>the difference between access tokens and refresh tokens,</li>



<li>what is a refresh token flow, </li>



<li>and advantages of such a flow. </li>
</ul>



<p>Of course, if you feel that&#8217;s not necessary in your case, then feel free to skip to the <em>Implement RefreshTokenRepository</em> section 😉 </p>



<h3 class="wp-block-heading" id="h-access-tokens-vs-refresh-tokens">Access Tokens vs Refresh Tokens</h3>



<p>Let&#8217;s start everything by distinguishing these two. </p>



<p>First of all, they both play distinct roles in token-based authentication systems. </p>



<p>An <strong>access token</strong> is a <strong>short-lived</strong> credential that is issued to a client after a successful authentication. Its purpose is to grant the client permission to access protected resources on behalf of the user. And by the client, we mean here a web app, a mobile app, or any other integration working with our REST API. </p>



<p>They are designed to have a limited lifespan to enhance security, reducing the risk associated with compromised tokens. </p>



<p><strong>Refresh tokens</strong>, on the other hand, are l<strong>ong-lived</strong> credentials that are used to obtain new access tokens without requiring the user to re-authenticate. </p>



<p>While access tokens are meant for short-term authorization, refresh tokens provide a mechanism for obtaining fresh access tokens and extending the user&#8217;s session securely. </p>



<h3 class="wp-block-heading" id="h-refresh-token-flow">Refresh Token Flow</h3>



<p>To put it simply, when a client authenticates successfully, it receives two tokens: an access token, and a refresh token. </p>



<p>The access token will be used to query our API. If it is compromised, the bad actor will have a limited time to perform unwanted actions on behalf of the user (thanks to its short-lived nature). </p>



<p>The refresh token, with a longer expiration time, will allow the client to get a new access token whenever it expires. This way, the user does not need to specify credentials (like username, and password for instance) every time an access token expires. </p>



<p>With this flow, we can strike a balance between usability and security. We improve user experience and maintain protection against unauthorized access.</p>



<h3 class="wp-block-heading" id="h-pros-vs-cons">Pros vs Cons </h3>



<p>As with every solution, refresh token flow has both pros and cons. </p>



<p>When it comes to the advantages, we already covered the first two- the shorter lifespan of access tokens <strong>limits the potential impact of a token compromise</strong>. Additionally, a long-living refresh token means a <strong>better user experience</strong>&#8211; a longer time between asking the user for his credentials. Lastly, this flow gives us <strong>more granular control</strong> over token management, for example, we can revoke refresh tokens selectively. </p>



<p>When it comes to the disadvantages, we could call them <strong>additional complexity</strong>. Not only we must carefully implement on the backend to prevent any vulnerabilities, but also, the storage and transmission of refresh tokens need to be handled securely to prevent unauthorized access.</p>



<p>Lastly, should we always use refresh token flow in our applications? </p>



<p>The answer is- as always- <strong>it depends</strong>. When dealing with security for our app we should spend some time investigating in depth all options on the table. Such an analysis would be way too much for the article about implementing a Ktor JWT refresh token flow 😉 </p>



<h2 class="wp-block-heading" id="h-implement-refreshtokenrepository">Implement RefreshTokenRepository</h2>



<p>With all of that said, we can finally start the practice part. </p>



<p>As the first step, let&#8217;s navigate to the <code>repository</code> package in our Ktor project and add the <code>RefreshTokenRepository</code> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class RefreshTokenRepository {

  private val tokens = mutableMapOf&lt;String, String>()

  fun findUsernameByToken(token: String): String? =
    tokens[token]

  fun save(token: String, username: String) {
    tokens[token] = username
  }

}</pre>



<p>Again, in a real-life scenario, this would be the class responsible for communicating to the data source, like a relational database, MongoDB, or any other source used in the project. To keep our tutorial simple and focused on the Ktor/JWT combination, we will use a mutable map storing a combination of tokens with usernames in memory. </p>



<p>As we can see, this class exposes two functions. One is for persisting the data in our map, and the second one is to retrieve usernames based on the JWT refresh token value. </p>



<h2 class="wp-block-heading" id="h-update-jwtservice">Update JwtService </h2>



<p>Nextly, let&#8217;s open up the <code>JwtService</code> class and update it a bit: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.codersee.model.User
import com.codersee.repository.UserRepository
import io.ktor.server.application.*
import io.ktor.server.auth.jwt.*
import java.util.*

class JwtService(
  private val application: Application,
  private val userRepository: UserRepository,
) {

  private val secret = getConfigProperty("jwt.secret")
  private val issuer = getConfigProperty("jwt.issuer")
  private val audience = getConfigProperty("jwt.audience")

  val realm = getConfigProperty("jwt.realm")

  val jwtVerifier: JWTVerifier =
    JWT
      .require(Algorithm.HMAC256(secret))
      .withAudience(audience)
      .withIssuer(issuer)
      .build()

  fun createAccessToken(username: String): String =
    createJwtToken(username, 3_600_000)

  fun createRefreshToken(username: String): String =
    createJwtToken(username, 86_400_000)

  private fun createJwtToken(username: String, expireIn: Int): String =
    JWT.create()
      .withAudience(audience)
      .withIssuer(issuer)
      .withClaim("username", username)
      .withExpiresAt(Date(System.currentTimeMillis() + expireIn))
      .sign(Algorithm.HMAC256(secret))


  fun customValidator(
    credential: JWTCredential,
  ): JWTPrincipal? {
    val username: String? = extractUsername(credential)
    val foundUser: User? = username?.let(userRepository::findByUsername)

    return foundUser?.let {
      if (audienceMatches(credential))
        JWTPrincipal(credential.payload)
      else
        null
    }
  }

  private fun audienceMatches(
    credential: JWTCredential,
  ): Boolean =
    credential.payload.audience.contains(audience)

  fun audienceMatches(
    audience: String
  ): Boolean =
    this.audience == audience

  private fun getConfigProperty(path: String) =
    application.environment.config.property(path).getString()

  private fun extractUsername(credential: JWTCredential): String? =
    credential.payload.getClaim("username").asString()
}</pre>



<p>First of all, we replaced the <code>UserService</code> dependency from the constructor with the <code>UserRepository</code>. This way, we would avoid a circular dependency between this class and <code>UserService</code> later. </p>



<p>Then, we extracted the code responsible for generating JWT tokens into a separate function- <code>createJwtToken</code>&#8211; which will be called by the <code>createAccessToken</code>, and <code>createRefreshToken</code>. Please note that these two functions declare different expiration times for both token types- 1 hour for the access token and 24 for the refresh token. </p>



<p>But what expiration time should we set in a real-life? Well, again, it depends 🙂 And we must consider both security concerns, as well as the <strong>storage</strong>. Yes, the longer the expiration time, the bigger the amount of refresh tokens stored in our database.  </p>



<p>Lastly, we added the <code>audienceMatches</code> function, which we will use later in the code. </p>



<h2 class="wp-block-heading" id="h-update-routing-layer">Update Routing Layer</h2>



<p>With all of that done, let&#8217;s navigate to the <code>routing</code> package and make the necessary changes too. </p>



<h3 class="wp-block-heading" id="h-add-request-response-classes">Add Request/Response Classes </h3>



<p>As the first step, let&#8217;s add a class responsible for mapping refresh token call JSON payload:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable

@Serializable
data class RefreshTokenRequest(
  val token: String,
)</pre>



<p>Following, let&#8217;s implement a class, which will serialize the authentication response:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable

@Serializable
data class AuthResponse(
  val accessToken: String,
  val refreshToken: String,
)</pre>



<p>And finally, the <code>RefreshTokenResponse</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable

@Serializable
data class RefreshTokenResponse(
  val token: String,
)</pre>



<h3 class="wp-block-heading" id="h-update-routing-kt">Update Routing.kt</h3>



<p>As the next step, let&#8217;s update the <code>Routing.kt</code> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.service.UserService
import io.ktor.server.application.*
import io.ktor.server.routing.*

fun Application.configureRouting(
  userService: UserService
) {
  routing {

    route("/api/auth") {
      authRoute(userService)
    }

    route("/api/user") {
      userRoute(userService)
    }

  }
}</pre>



<p>As we can see, we don&#8217;t inject the <code>JwtService</code> anymore. Instead, we invoke the <code>authRoute</code> with <code>UserService</code> instance instead. </p>



<p><h3 class="wp-block-heading" id="h-update-routing-kt">Edit AuthRoute.kt</h3>With that done, let&#8217;s update the <code>AuthRoute</code> file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.routing.request.LoginRequest
import com.codersee.routing.request.RefreshTokenRequest
import com.codersee.routing.response.AuthResponse
import com.codersee.routing.response.RefreshTokenResponse
import com.codersee.service.UserService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun Route.authRoute(userService: UserService) {

  post {
    val loginRequest = call.receive&lt;LoginRequest>()

    val authResponse: AuthResponse? = userService.authenticate(loginRequest)

    authResponse?.let {
      call.respond(authResponse)
    } ?: call.respond(
      message = HttpStatusCode.Unauthorized
    )
  }

  post("/refresh") {
    val request = call.receive&lt;RefreshTokenRequest>()

    val newAccessToken = userService.refreshToken(token = request.token)

    newAccessToken?.let {
      call.respond(
        RefreshTokenResponse(it)
      )
    } ?: call.respond(
      message = HttpStatusCode.Unauthorized
    )
  }

}</pre>



<p>So, instead of dealing with <code>JwtService</code>, we are going to invoke the <code>UserService</code> functions instead. </p>



<p>In the first handler for <code>POST /api/auth</code> calls, we invoke the <code>authenticate</code> method and if we receive an <code>AuthResponse</code>, then we will simply return that to the API client with <code>200 OK</code> status. Otherwise, we will return <code>401 Unauthorized</code>. </p>



<p>Additionally, we add a new handler for <code>POST /api/auth/refresh</code> calls. Inside it, we read the JSON payload and create an instance of <code>RefreshTokenRequest</code>. Then, we invoke the <code>refreshToken</code> method from <code>UserService</code> (which we are going to implement in a moment). And just like previously, if the service returns a non-null object, then we respond with <code>200 OK</code> and with <code>401 Unauthorized</code> if that&#8217;s not the case. </p>



<h2 class="wp-block-heading" id="h-update-userservice">Update UserService</h2>



<p>As the last thing in our Ktor with the JWT refresh token tutorial, we must update the <code>UserService</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.auth0.jwt.interfaces.DecodedJWT
import com.codersee.model.User
import com.codersee.repository.RefreshTokenRepository
import com.codersee.repository.UserRepository
import com.codersee.routing.request.LoginRequest
import com.codersee.routing.response.AuthResponse
import java.util.*

class UserService(
  private val userRepository: UserRepository,
  private val jwtService: JwtService,
  private val refreshTokenRepository: RefreshTokenRepository
) {

  fun findAll(): List&lt;User> =
    userRepository.findAll()

  fun findById(id: String): User? =
    userRepository.findById(
      id = UUID.fromString(id)
    )

  fun findByUsername(username: String): User? =
    userRepository.findByUsername(username)

  fun save(user: User): User? {
    val foundUser = userRepository.findByUsername(user.username)

    return if (foundUser == null) {
      userRepository.save(user)
      user
    } else null
  }

  fun authenticate(loginRequest: LoginRequest): AuthResponse? {
    val username = loginRequest.username
    val foundUser: User? = userRepository.findByUsername(username)

    return if (foundUser != null &amp;&amp; loginRequest.password == foundUser.password) {
      val accessToken = jwtService.createAccessToken(username)
      val refreshToken = jwtService.createRefreshToken(username)

      refreshTokenRepository.save(refreshToken, username)

      AuthResponse(
        accessToken = accessToken,
        refreshToken = refreshToken,
      )
    } else
      null
  }

  fun refreshToken(token: String): String? {
    val decodedRefreshToken = verifyRefreshToken(token)
    val persistedUsername = refreshTokenRepository.findUsernameByToken(token)

    return if (decodedRefreshToken != null &amp;&amp; persistedUsername != null) {
      val foundUser: User? = userRepository.findByUsername(persistedUsername)
      val usernameFromRefreshToken: String? = decodedRefreshToken.getClaim("username").asString()

      if (foundUser != null &amp;&amp; usernameFromRefreshToken == foundUser.username)
        jwtService.createAccessToken(persistedUsername)
      else
        null
    } else
      null
  }

  private fun verifyRefreshToken(token: String): DecodedJWT? {
    val decodedJwt: DecodedJWT? = getDecodedJwt(token)

    return decodedJwt?.let {
      val audienceMatches = jwtService.audienceMatches(it.audience.first())

      if (audienceMatches)
        decodedJwt
      else
        null
    }
  }

  private fun getDecodedJwt(token: String): DecodedJWT? =
    try {
      jwtService.jwtVerifier.verify(token)
    } catch (ex: Exception) {
      null
    }
}</pre>



<p>And let&#8217;s see the most important changes here. </p>



<p>Firstly, we add the <code>authenticate</code> function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun authenticate(loginRequest: LoginRequest): AuthResponse? {
  val username = loginRequest.username
  val foundUser: User? = userRepository.findByUsername(username)

  return if (foundUser != null &amp;&amp; loginRequest.password == foundUser.password) {
    val accessToken = jwtService.createAccessToken(username)
    val refreshToken = jwtService.createRefreshToken(username)

    refreshTokenRepository.save(refreshToken, username)

    AuthResponse(
      accessToken = accessToken,
      refreshToken = refreshToken,
    )
  } else
    null
}</pre>



<p>This one is simply responsible for fetching a user from our repository using the passed <code>username</code>. </p>



<p></p>



<p>If we find it successfully, we check if the password matches (again, it&#8217;s just a dummy plain text comparison and you want to learn more about password checks in real-life scenarios). And if that&#8217;s the case, then we simply return the access and refresh tokens to the user and persist the refresh token in our &#8220;database&#8221;. </p>



<p>Nextly, we have a refresh token flow function: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun refreshToken(token: String): String? {
  val decodedRefreshToken = verifyRefreshToken(token)
  val persistedUsername = refreshTokenRepository.findUsernameByToken(token)

  return if (decodedRefreshToken != null &amp;&amp; persistedUsername != null) {
    val foundUser: User? = userRepository.findByUsername(persistedUsername)
    val usernameFromRefreshToken: String? = decodedRefreshToken.getClaim("username").asString()

    if (foundUser != null &amp;&amp; usernameFromRefreshToken == foundUser.username)
      jwtService.createAccessToken(persistedUsername)
    else
      null
  } else
    null
}</pre>



<p>When it comes to refreshing a token, we check if there is any persisted username found by the token value. If it is, then we fetch the user by the given username (this way, we won&#8217;t refresh a token for user that does not exist anymore). </p>



<p>And at this point, we can <strong>rerun </strong>our application and <strong>verify </strong>if our logic works, as expected. Again, if you&#8217;d like to get a ready-to-go Postman collection, then please let me know <strong>in the comments section</strong>.</p>



<h2 class="wp-block-heading" id="h-ktor-with-jwt-refresh-token-summary">Ktor With JWT Refresh Token Summary</h2>



<p>And basically, that&#8217;s all for this, second tutorial in a series related to Ktor security, in which we learned how to create a <strong>Ktor application with JWT refresh token flow</strong>. </p>



<p>As always, you can find the whole source code in my <a href="https://github.com/codersee-blog/kotlin-ktor-jwt-access-refresh-tokens" target="_blank" rel="noreferrer noopener">GitHub repository</a>. </p>



<p>Thank you for being here and please do not forget to share it with other people on your social media (it will help me a lot) and check other posts from the series:</p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/">Secure REST API with Ktor and JWT Access Tokens</a>, </li>



<li>and <a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/" target="_blank" rel="noreferrer noopener">how to add a<strong> role-based access control (RBAC)</strong> to the project</a>. </li>
</ul>
<p>The post <a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/">Secure Ktor app with JWT refresh tokens. Refresh token flow.</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Secure REST API with Ktor and JWT Access Tokens</title>
		<link>https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/</link>
					<comments>https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 21 Nov 2023 08:00:00 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Authorization]]></category>
		<category><![CDATA[JWT]]></category>
		<category><![CDATA[REST]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9008343</guid>

					<description><![CDATA[<p>In this, first article in a series, I will show you how to create a REST API with Ktor and secure it with JWT (JSON Web Token) access tokens.</p>
<p>The post <a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/">Secure REST API with Ktor and JWT Access Tokens</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Hello and welcome to the <strong>first article in a series</strong>. This time, I will show you how to implement a simple <strong>REST API with Ktor</strong> and secure it with <strong>JWT (JSON Web Token) access tokens</strong>.</p>



<p>To be even more specific, in this tutorial I will teach you:</p>



<ul class="wp-block-list">
<li>how to implement a simple <strong>REST API</strong>, </li>



<li>what are <strong>JSON Web Tokens</strong> and what does the <strong>authentication</strong> mean, </li>



<li>how to <strong>generate </strong>and <strong>validate JWT access tokens</strong>, and <strong>authenticate </strong>users, </li>



<li>and how to read data using the <strong>JWTPrincipal</strong>.</li>
</ul>



<p>In the upcoming articles, we will continue the topic and learn additionally: </p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/" target="_blank" rel="noreferrer noopener">how to implement a <strong>refresh token flow </strong>with Ktor</a>,</li>



<li>and <a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/" target="_blank" rel="noreferrer noopener">how to add a<strong> role-based access control (RBAC)</strong> to the project</a>. </li>
</ul>



<h2 class="wp-block-heading" id="h-video-tutorial">Video Tutorial</h2>



<p>If you prefer <strong>video content</strong>, then check out my video:</p>



<div style="text-align: center; width: 90%; margin-left: 5%;">
<a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FWmURqVIWZeQ%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p></div>



<p>If you find this content useful,<strong> please leave a subscription&nbsp;</strong> 😉</p>



<h2 class="wp-block-heading" id="h-what-is-authentication">What Is Authentication? </h2>



<p>But before we start implementing our Ktor app with JWT tokens, let me quickly explain <strong>what exactly authentication is.</strong> </p>



<p>In essence, it is the process of <strong>verifying the identity</strong> of a user, system, or device <strong>to ensure that they are who they claim to be</strong>. </p>



<p>It is like having a secret code to enter a club. Let&#8217;s imagine we want to get into a cool party, and at the entrance, there&#8217;s a bouncer checking everyone&#8217;s membership. To prove we belong, we provide a special password. If the password matches what&#8217;s on the list, we&#8217;re in! </p>



<p>In the digital world, it&#8217;s the same idea &#8211; we need to confirm our identity with a unique key (like a password or JWT token) to access our email, bank account, or, like in our case, any resource through the exposed REST API. </p>



<p>Of course, <strong>authentication<em> </em></strong>is not a synonym for <strong>authorization</strong> (but we will get back to that in the upcoming post). </p>



<h2 class="wp-block-heading" id="h-jwt-tokens-explained">JWT Tokens Explained</h2>



<p>As the last thing before we head to the practice, let&#8217;s learn a bit about the JWT tokens. </p>



<p><strong>JWTs</strong>, also known as <strong>JSON Web Tokens</strong>, are nothing else than one of plenty of existing ways to authenticate users. </p>



<p>Just like with our previous example- in real life, we can use our ID, passport, or in other cases, some secret word. </p>



<p>Here, the JWT is like a digital secret handshake on the internet. When sign in using our username and password, the server gives us a special JWT, and later, we can use it to access a REST API. This way, we don&#8217;t need to specify such vulnerable data every time we want to make a request. </p>



<p>What are the other advantages? Well, they are <strong>safe and compact, </strong>and allow us to <strong>hold information</strong>. </p>



<p>JWTs consist of three parts separated by dots: header.payload.signature: </p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="534" src="http://blog.codersee.com/wp-content/uploads/2023/10/spring_boot_3_security_jwt_io-1024x534.png" alt="The image is a screenshot from jwt.io page and present and example encoded JWT token with its decoded value." class="wp-image-9008239" srcset="https://blog.codersee.com/wp-content/uploads/2023/10/spring_boot_3_security_jwt_io-1024x534.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/10/spring_boot_3_security_jwt_io-300x156.png 300w, https://blog.codersee.com/wp-content/uploads/2023/10/spring_boot_3_security_jwt_io-768x400.png 768w, https://blog.codersee.com/wp-content/uploads/2023/10/spring_boot_3_security_jwt_io.png 1191w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The encoded value can be easily decoded and JWT token values can be read. </p>



<p>The <strong>header </strong>typically consists of two parts: the type of the token (missing above), and the signing algorithm being used (HMAC SHA512).</p>



<p>The second part of the token is the payload, which contains the <strong>claims</strong>. Claims, Claims are statements about an entity (typically, the user) and additional data.</p>



<p>Lastly, the <strong>signature</strong> is used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn&#8217;t changed along the way.</p>



<h2 class="wp-block-heading" id="h-set-up-new-ktor-project">Set Up New Ktor Project</h2>



<p>With all of that said, let&#8217;s navigate to the <a href="https://start.ktor.io/" target="_blank" rel="noreferrer noopener">Ktor Project Generator</a> and start by specifying the necessary details: </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="731" height="851" src="http://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_1.png" alt="Image is a screenshot from the Ktor Project Generator page and shows the first step of generator settings, like Ktor version, engine set to Netty, or HOCON file config. " class="wp-image-9008354" srcset="https://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_1.png 731w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_1-258x300.png 258w" sizes="auto, (max-width: 731px) 100vw, 731px" /></figure>



<p>At this stage, we must specify the basic things, like the Ktor version, engine, etc. </p>



<p>Nextly, let&#8217;s click the &#8220;Add plugins&#8221; button and configure the following plugins (aka dependencies):</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="630" height="750" src="http://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_2.png" alt="Image shows all added plugins in the configurator: authentication, authentication JWT, content negotiation, kotlinx.serialization, and routing." class="wp-image-9008355" srcset="https://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_2.png 630w, https://blog.codersee.com/wp-content/uploads/2023/11/ktor_jwt_authentication_generate_project_2-252x300.png 252w" sizes="auto, (max-width: 630px) 100vw, 630px" /></figure>



<p>As we can see, in order to expose a REST API and secure it with JWT in Ktor, we will need the following: </p>



<ul class="wp-block-list">
<li>Authentication,</li>



<li>Authentication JWT, </li>



<li>Content Negotiation,</li>



<li>kotlinx.serialization,</li>



<li>and Routing. </li>
</ul>



<p>With that done, let&#8217;s generate the project, download the ZIP file, extract it, and import it to the IntelliJ IDEA. </p>



<p>When we open up the project, we should see that there are some plugins already configured inside the <code>plugins</code> package and <code>Application.kt</code> file. </p>



<p>Let&#8217;s delete files inside the <code>plugins</code> and clean up the <code>Application.kt</code> to be on the same page:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.ktor.server.application.*

fun main(args: Array&lt;String>) {
  io.ktor.server.netty.EngineMain.main(args)
}

fun Application.module() {
}</pre>



<h2 class="wp-block-heading" id="h-expose-users-api">Expose Users API </h2>



<p>As the next step, let&#8217;s expose the API responsible for <strong>user management</strong>.</p>



<h3 class="wp-block-heading" id="h-add-user-model">Add User Model</h3>



<p>Firstly, let&#8217;s create the <code>model</code> package and implement the <code>User.kt</code> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import java.util.UUID

data class User(
  val id: UUID,
  val username: String,
  val password: String
)</pre>



<p>As can be seen, it&#8217;s just a simple class with the <em>id, username, </em>and <em>password </em>fields.</p>



<h3 class="wp-block-heading" id="h-create-userrepository">Create UserRepository</h3>



<p>Secondly, we must add a class responsible for user storage and retrieval. </p>



<p>To do so, let&#8217;s add the <code>repository</code> package and put the <code>UserRepository</code> inside it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.model.User
import java.util.*

class UserRepository {

  private val users = mutableListOf&lt;User>()

  fun findAll(): List&lt;User> =
    users

  fun findById(id: UUID): User? =
    users.firstOrNull { it.id == id }

  fun findByUsername(username: String): User? =
    users.firstOrNull { it.username == username }

  fun save(user: User): Boolean =
    users.add(user)
}</pre>



<p>Normally, we would put a code responsible for &#8220;talking&#8221; to the database right here. </p>



<p>In our case, to not lose focus from the clue of this article- authentication in Ktor and JWT- we introduce a dummy repository, which uses the mutable list to store users in memory. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite><strong>Note:</strong> in our dummy repository, we store user password in a plain text. <br><br>Nevertheless, in real-life scenarios we should always encrypt it before persisting. To learn a bit more about different encryption algorithms, you can check out my other post in which I explain <a href="https://blog.codersee.com/kotlin-pbkdf2-secure-password-hashing/">PBKDF2 hashing</a>.</cite></blockquote>



<h3 class="wp-block-heading" id="h-implement-userservice">Implement UserService</h3>



<p>With that done, let&#8217;s introduce a service layer in our project. </p>



<p>So, similarly, let&#8217;s add the <code>service</code> package and the <code>UserService</code> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.model.User
import com.codersee.repository.UserRepository
import java.util.*

class UserService(
  private val userRepository: UserRepository
) {

  fun findAll(): List&lt;User> =
    userRepository.findAll()

  fun findById(id: String): User? =
    userRepository.findById(
      id = UUID.fromString(id)
    )

  fun findByUsername(username: String): User? =
    userRepository.findByUsername(username)

  fun save(user: User): User? {
    val foundUser = userRepository.findByUsername(user.username)

    return if (foundUser == null) {
      userRepository.save(user)
      user
    } else null
  }
}</pre>



<p>This time, we expose 4 functions, which we will utilize later when handling requests and working with JWT tokens. </p>



<h3 class="wp-block-heading" id="h-add-request-and-response-classes">Add Request and Response Classes</h3>



<p>Before we expose our REST endpoint, let&#8217;s take care of the request and response classes. </p>



<p>Let&#8217;s start by creating the <code>routing.request</code> package and implementing the <code>UserRequest</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable

@Serializable
data class UserRequest(
  val username: String,
  val password: String,
)</pre>



<p>As we can see, we must annotate objects that we want to <strong>serialize</strong> or <strong>deserialize</strong> using the <strong>@Serializable</strong> when working with <code>kotlinx.serialization</code> library. </p>



<p>As a result, the serialization plugin will automatically generate the implementation of <em>KSerializer</em>. </p>



<p>Similarly, let&#8217;s add the <code>routing.response</code> package and the <code>UserResponse</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable
import java.util.*

@Serializable
data class UserResponse(
  val id: UUID,
  val username: String,
)</pre>



<p>This one seems pretty similar, but <strong>unfortunately</strong>, we must do one more thing here.</p>



<h3 class="wp-block-heading" id="h-custom-uuid-kserializer">Custom UUID KSerializer</h3>



<p>If we would try to use this class as it is, we would end up with <strong>the following error:</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>Serializer has not been found for type &#8216;UUID&#8217;. To use context serializer as fallback, explicitly annotate type or property with @Contextual.</cite></blockquote>



<p>And to fix this issue, <strong>we must implement our own UUID serializer</strong>.</p>



<p>So as the first step, let&#8217;s introduce the <code>util</code> package and add the <code>UUIDSerializer</code> object: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.util.*

object UUIDSerializer : KSerializer&lt;UUID> {

  override val descriptor: SerialDescriptor
    get() = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)

  override fun deserialize(decoder: Decoder): UUID =
    UUID.fromString(decoder.decodeString())

  override fun serialize(encoder: Encoder, value: UUID) =
    encoder.encodeString(value.toString())
}</pre>



<p>Following, let&#8217;s instruct the plugin to use our implementation whenever it wants to serialize the <code>id</code> field:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.util.UUIDSerializer
import kotlinx.serialization.Serializable
import java.util.*

@Serializable
data class UserResponse(
  @Serializable(with = UUIDSerializer::class)
  val id: UUID,
  val username: String,
)</pre>



<h3 class="wp-block-heading" id="h-register-serialization-plugin">Register Serialization Plugin</h3>



<p>When working with Ktor, we must explicitly specify the plugins we use. There is no component scan based on the dependencies which you may know from Spring Boot 🙂 </p>



<p>So as the next step, let&#8217;s add the <code>Seriaization.kt</code> to the <code>plugins</code> package: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*

fun Application.configureSerialization() {
  install(ContentNegotiation) {
    json()
  }
}</pre>



<p>The <code>ContentNegotiation</code> plugin has two tasks: </p>



<ul class="wp-block-list">
<li>it negotiates the media types between the client and server, </li>



<li>and serializes/deserializes the content in a specific format. </li>
</ul>



<p>In our case, we can clearly see that we will be using the <code>JSON</code> format. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>Note: other formats, like XML, CBOR, and ProtoBuf are supported, too. </cite></blockquote>



<p>Lastly, we must make use of our extension function inside the <code>Application.kt</code> file: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.plugins.configureSerialization
import io.ktor.server.application.*

fun main(args: Array&lt;String>) {
  io.ktor.server.netty.EngineMain.main(args)
}

fun Application.module() {
  configureSerialization()
}</pre>



<h3 class="wp-block-heading" id="h-add-user-route">Add User Route</h3>



<p>With all of that being done, we can finally expose endpoints for user management. </p>



<p>As the first step, let&#8217;s implement the <code>Routing.kt</code> class inside the <code>routing</code> package:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.service.UserService
import io.ktor.server.application.*
import io.ktor.server.routing.*

fun Application.configureRouting(
  userService: UserService
) {
  routing {

    route("/api/user") {
      userRoute(userService)
    }

  }
}</pre>



<p>This way, we instruct Ktor that whenever a request is made to the <code>/api/user**</code>, it should look for a handler inside the <code>userRoute</code> (which we will add in a moment). </p>



<p>Following, let&#8217;s get back to the <code>Application.kt</code> and register our routing: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.plugins.configureSerialization
import com.codersee.repository.UserRepository
import com.codersee.routing.configureRouting
import com.codersee.service.UserService
import io.ktor.server.application.*

fun main(args: Array&lt;String>) {
  io.ktor.server.netty.EngineMain.main(args)
}

fun Application.module() {
  val userRepository = UserRepository()
  val userService = UserService(userRepository)

  configureSerialization()
  configureRouting(userService)
}</pre>



<p>Nextly, let&#8217;s add the <code>UserRoute.kt</code> inside the <code>routing</code> package:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.model.User
import com.codersee.routing.request.UserRequest
import com.codersee.routing.response.UserResponse
import com.codersee.service.UserService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*

fun Route.userRoute(userService: UserService) {

  post {
    val userRequest = call.receive&lt;UserRequest>()

    val createdUser = userService.save(
      user = userRequest.toModel()
    ) ?: return@post call.respond(HttpStatusCode.BadRequest)

    call.response.header(
      name = "id",
      value = createdUser.id.toString()
    )
    call.respond(
      message = HttpStatusCode.Created
    )
  }

  get {
    val users = userService.findAll()

    call.respond(
      message = users.map(User::toResponse)
    )
  }

  get("/{id}") {
    val id: String = call.parameters["id"]
      ?: return@get call.respond(HttpStatusCode.BadRequest)

    val foundUser = userService.findById(id)
      ?: return@get call.respond(HttpStatusCode.NotFound)

    call.respond(
      message = foundUser.toResponse()
    )
  }
}

private fun UserRequest.toModel(): User =
  User(
    id = UUID.randomUUID(),
    username = this.username,
    password = this.password
  )

private fun User.toResponse(): UserResponse =
  UserResponse(
    id = this.id,
    username = this.username,
  )</pre>



<p>And to be on the same page, let&#8217;s rephrase what is going on here:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.model.User
import com.codersee.routing.request.UserRequest
import com.codersee.routing.response.UserResponse
import com.codersee.service.UserService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.util.*

fun Route.userRoute(userService: UserService) {

  post {
    val userRequest = call.receive&lt;UserRequest>()

    val createdUser = userService.save(
      user = userRequest.toModel()
    ) ?: return@post call.respond(HttpStatusCode.BadRequest)

    call.response.header(
      name = "id",
      value = createdUser.id.toString()
    )
    call.respond(
      message = HttpStatusCode.Created
    )
  }

}</pre>



<p>Firstly, we inject the instance of <code>UserService</code>, which we will use to save and retrieve users. </p>



<p>Nextly, we specify a handler for <strong>POST requests</strong>. </p>



<p>Inside this handler, we get the JSON payload sent by the API consumer as an instance of <em>UserRequest</em> with the <code>call.receive&lt;T&gt;()</code> function. Then, we invoke the <code>save</code> function and pass the instance of the <code>User</code> class, which we obtain using the <code>toModel()</code> extension function. If the <code>save</code> returns a <em>null</em> value, we return <code>400 Bad Request</code> with the <code>call.respond()</code> function. </p>



<p>On the other hand, if the user was saved successfully, we respond with a <code>201 Created</code> status and pass its identifier as the <code>id</code> header value with <code>call.response.header()</code> function. </p>



<p>Then, we have another handler responsible for <code>GET /api/user</code> requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">get {
  val users = userService.findAll()

  call.respond(
    message = users.map(User::toResponse)
  )
}</pre>



<p>It is responsible for fetching all users and returning them after mapping with <code>toResponse()</code> extension functions. </p>



<p>Lastly, we have the handler answering to the <code>GET /api/user/{id}</code> requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">get("/{id}") {
  val id: String = call.parameters["id"]
    ?: return@get call.respond(HttpStatusCode.BadRequest)

  val foundUser = userService.findById(id)
    ?: return@get call.respond(HttpStatusCode.NotFound)

  call.respond(
    message = foundUser.toResponse()
  )
}</pre>



<p>This time, we read the <strong>path variable</strong> <code>id</code> using the <code>call.paramaters</code>. If is not present, we return a <code>400 Bad Request</code> response. </p>



<p>Following, we try to find a user by ID and if we fail, then we simply return the <code>404 Not Found</code>. </p>



<h2 class="wp-block-heading" id="h-ktor-with-jwt">Ktor With JWT </h2>



<p>Excellent! At this point, we have a working API, so it&#8217;s finally time to learn a bit more about how to secure it, and how to generate JWT tokens in our Ktor application so that we can use them later to access these endpoints. </p>



<h3 class="wp-block-heading" id="h-update-config-file">Update Config File</h3>



<p>As the first step, let&#8217;s navigate to the <code>application.conf</code> file. </p>



<p>Among other settings, we should see the following <code>jwt</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">jwt {
    domain = "https://jwt-provider-domain/"
    audience = "jwt-audience"
    realm = "ktor sample app"
}</pre>



<p>Let&#8217;s slightly modify it:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">jwt {
    audience = "my-audience"
    issuer = "http://localhost/"
    realm = "My realm"
    secret = ${SECRET}
}</pre>



<p>Without going into too much detail, these values (except for the <strong>secret</strong>) will be <strong>claims</strong> in our JWT tokens: </p>



<ul class="wp-block-list">
<li><strong>audience</strong>&#8211; which describes the recipient of the token, </li>



<li><strong>issuer</strong>&#8211; which is the entity that issues the token,</li>



<li><strong>realm</strong>&#8211; which is an optional parameter providing additional context or scope,</li>



<li><strong>secret</strong>&#8211; which is a secret key used for token signing and verification.</li>
</ul>



<p>Please note that the <code>secret</code> value <strong>IS NOT </strong>hardcoded (and should never be). We will source it from the environment variable called <code>SECRET</code>.</p>



<h2 class="wp-block-heading" id="h-implement-jwtservice">Implement JwtService</h2>



<p>With that done, we can navigate to the <code>service</code> package and add the <code>JwtService</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.codersee.model.User
import io.ktor.server.application.*
import io.ktor.server.auth.jwt.*
import java.util.*

class JwtService(
  private val application: Application,
  private val userService: UserService,
) {

  private val secret = getConfigProperty("jwt.secret")
  private val issuer = getConfigProperty("jwt.issuer")
  private val audience = getConfigProperty("jwt.audience")

  val realm = getConfigProperty("jwt.realm")

  val jwtVerifier: JWTVerifier =
    JWT
      .require(Algorithm.HMAC256(secret))
      .withAudience(audience)
      .withIssuer(issuer)
      .build()

  fun createJwtToken(loginRequest: LoginRequest): String? {
    val foundUser: User? = userService.findByUsername(loginRequest.username)

    return if (foundUser != null &amp;&amp; loginRequest.password == foundUser.password)
      JWT.create()
        .withAudience(audience)
        .withIssuer(issuer)
        .withClaim("username", loginRequest.username)
        .withExpiresAt(Date(System.currentTimeMillis() + 3_600_000))
        .sign(Algorithm.HMAC256(secret))
    else
      null
  }

  fun customValidator(
    credential: JWTCredential,
  ): JWTPrincipal? {
    val username: String? = extractUsername(credential)
    val foundUser: User? = username?.let(userService::findByUsername)

    return foundUser?.let {
      if (audienceMatches(credential))
        JWTPrincipal(credential.payload)
      else
        null
    }
  }

  private fun audienceMatches(
    credential: JWTCredential,
  ): Boolean =
    credential.payload.audience.contains(audience)

  private fun getConfigProperty(path: String) =
    application.environment.config.property(path).getString()

  private fun extractUsername(credential: JWTCredential): String? =
    credential.payload.getClaim("username").asString()
}
</pre>



<p>Again, let&#8217;s break it down into smaller chunks: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.auth0.jwt.JWT
import com.auth0.jwt.JWTVerifier
import com.auth0.jwt.algorithms.Algorithm
import com.codersee.model.User
import io.ktor.server.application.*
import io.ktor.server.auth.jwt.*
import java.util.*

class JwtService(
  private val application: Application,
  private val userService: UserService,
) {

  private val secret = getConfigProperty("jwt.secret")
  private val issuer = getConfigProperty("jwt.issuer")
  private val audience = getConfigProperty("jwt.audience")

  val realm = getConfigProperty("jwt.realm")

  val jwtVerifier: JWTVerifier =
    JWT
      .require(Algorithm.HMAC256(secret))
      .withAudience(audience)
      .withIssuer(issuer)
      .build()

  private fun getConfigProperty(path: String) =
    application.environment.config.property(path).getString()
}</pre>



<p>Firstly, we inject the instance of <code>Application</code> and <code>UserService</code> classes. </p>



<p>Following we read the configuration properties we set in the previous step. 3 of them can be private, but the <code>realm</code> will be later used in another class to configure security. </p>



<p>After that, we must create an instance of the <code>JwtVerifier</code> and configure it. In simple words, this class holds the verify method which will later verify that a given token has not only a proper JWT format but also its signature matches. </p>



<p>And this is why we specify which <strong>algorithm</strong>, <strong>audience</strong>, and <strong>issuer</strong> a valid token must have.</p>



<p>With all of that done, we introduce the <em>createJwtToken</em> function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createJwtToken(loginRequest: LoginRequest): String? {
  val foundUser: User? = userService.findByUsername(loginRequest.username)

  return if (foundUser != null &amp;&amp; loginRequest.password == foundUser.password)
    JWT.create()
      .withAudience(audience)
      .withIssuer(issuer)
      .withClaim("username", loginRequest.username)
      .withExpiresAt(Date(System.currentTimeMillis() + 3_600_000))
      .sign(Algorithm.HMAC256(secret))
  else
    null
}</pre>



<p>This one takes the <code>username</code> as an argument and tries to find a user with it. </p>



<p>If we succeed and the password matches, then we produce a new JWT access token with the appropriate <strong>audience, issuer, </strong>and <strong>username</strong>, expiring in <strong>60 minutes</strong> and signed with <strong>the HMAC-SHA256</strong> algorithm (with our secret key).</p>



<p>Lastly, we add the <code>customValidator</code> function, which we will later use to add an <strong>additional validation:</strong></p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun customValidator(
  credential: JWTCredential,
): JWTPrincipal? {
  val username: String? = extractUsername(credential)
  val foundUser: User? = username?.let(userService::findByUsername)

  return foundUser?.let {
    if (audienceMatches(credential))
      JWTPrincipal(credential.payload)
    else
      null
  }
}

private fun audienceMatches(
  credential: JWTCredential,
): Boolean =
  credential.payload.audience.contains(audience)

private fun extractUsername(credential: JWTCredential): String? =
  credential.payload.getClaim("username").asString()</pre>



<p>As the first thing, we extract the <em>username</em> from the JWT token and we try to find a user by this value. </p>



<p>If we succeed, then we check if the <em>audience </em>from the token matches the audience set in our Ktor project. If that&#8217;s true, then we instantiate the <strong>JWTPrincipal</strong>, which we will use later to obtain the information. </p>



<h2 class="wp-block-heading" id="h-install-authentication-plugin">Install Authentication Plugin</h2>



<p>At this point, we have the JWT service ready, so it&#8217;s time to install the Authentication plugin. </p>



<p>To do so, let&#8217;s add the <code>Security</code> plugin inside the <code>plugins</code> package:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.service.JwtService
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*

fun Application.configureSecurity(
  jwtService: JwtService
) {
  authentication {
    jwt {
      realm = jwtService.realm
      verifier(jwtService.jwtVerifier)

      validate { credential ->
        jwtService.customValidator(credential, jwtService)
      }
    }
  }
}</pre>



<p>But what exactly is going on here? </p>



<p>The above code installs the Authentication plugin (<code>authentication</code>) with JWT Authentication provider (<code>jwt</code>). </p>



<p>Additionally, we must specify the <code>realm</code>, <code>verifier</code>, and the custom <code>validate</code> function that we implemented previously. </p>



<p>And what if we would like to have <strong>multiple providers in our project?</strong></p>



<p>Well, we can do that easily by simply specifying a new one with a name:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.service.JwtService
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.auth.jwt.*

fun Application.configureSecurity(
  jwtService: JwtService
) {
  authentication {
    jwt {
      realm = jwtService.realm
      verifier(jwtService.jwtVerifier)

      validate { credential ->
        jwtService.customValidator(credential, jwtService)
      }
    }

    jwt("another-auth") {
      realm = jwtService.realm
      verifier(jwtService.jwtVerifier)

      validate { credential ->
        jwtService.customValidator(credential, jwtService)
      }
    }
  }
}</pre>



<p>Later, we will see how to make use of these JWT auth providers, but for now, let&#8217;s navigate to the <code>Application.kt</code> file and make use of our new config:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.plugins.configureSecurity
import com.codersee.plugins.configureSerialization
import com.codersee.repository.UserRepository
import com.codersee.routing.configureRouting
import com.codersee.service.JwtService
import com.codersee.service.UserService
import io.ktor.server.application.*

fun main(args: Array&lt;String>) {
  io.ktor.server.netty.EngineMain.main(args)
}

fun Application.module() {
  val userRepository = UserRepository()
  val userService = UserService(userRepository)
  val jwtService = JwtService(this, userService)

  configureSerialization()
  configureSecurity(jwtService)
  configureRouting(jwtService, userService)
}</pre>



<h2 class="wp-block-heading" id="h-expose-login-endpoint">Expose Login Endpoint</h2>



<p>Perfect! At this point we have everything we need to expose a new endpoint- <code>POST /api/auth</code>&#8211; which we will use to get JWT access tokens for our Ktor app based on the passed username and password. </p>



<p>So as the first step, let&#8217;s go to the <code>Routing</code> file, inject the <code>JwtService</code> and prepare a new route:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.service.JwtService
import com.codersee.service.UserService
import io.ktor.server.application.*
import io.ktor.server.routing.*

fun Application.configureRouting(
  jwtService: JwtService,
  userService: UserService
) {
  routing {

    route("/api/auth") {
      authRoute(jwtService)
    }

    route("/api/user") {
      userRoute(userService)
    }

  }
}</pre>



<p>Nextly, let&#8217;s implement the <code>LoginRequest</code> inside the <code>routing.request</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import kotlinx.serialization.Serializable

@Serializable
data class LoginRequest(
  val username: String,
  val password: String,
)</pre>



<p>No traps here this time, so let&#8217;s create the <code>AuthRoute.kt</code> file: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import com.codersee.routing.request.LoginRequest
import com.codersee.service.JwtService
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun Route.authRoute(jwtService: JwtService) {

  post {
    val user = call.receive&lt;LoginRequest>()

    val token: String? = jwtService.createJwtToken(user.username)

    token?.let {
      call.respond(hashMapOf("token" to token))
    } ?: call.respond(
      message = HttpStatusCode.Unauthorized
    )
  }

}</pre>



<p>This time, we read the request payload to the <code>LoginRequest</code> instance and we try to create a new JWT access token based on the <em>username</em>. </p>



<p>If we succeed (meaning a user with the following username was found and the password matches), then we return a fresh JWT access token. Otherwise, we simply return <code>401 Unauthorized</code> to the API consumer.</p>



<p>At this point, we can test the app, for example by using curl (and if you would like to get a full Postman collection with automated token handling, then please leave a comment for this article).</p>



<p>Let&#8217;s start by creating a new user:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/api/user' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "user", 
    "password": "password"
}'</pre>



<p>As a response, we should get <code>201 Created</code> with the id in the response header. </p>



<p>Following, let&#8217;s try to get a JWT access token:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/api/auth' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "user", 
    "password": "password"
}'

// Response:

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJteS1hdWRpZW5jZSIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3QvIiwidXNlcm5hbWUiOiJ1c2VyIiwiZXhwIjoxNzAwMTU2Mzg4fQ.DG8SsNrQI5-VqrZeZMFL4IRuVSLeo9Zs9dEifti9nvQ"
}</pre>



<p>Excellent! So far so good. </p>



<p>You can even take this token and check it out using the <a href="https://jwt.io/">jwt.io page</a> 🙂</p>



<h2 class="wp-block-heading" id="h-secure-user-endpoint">Secure User Endpoint</h2>



<p>Finally, let&#8217;s secure user API endpoints: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.userRoute(userService: UserService) {

  post {
    val userRequest = call.receive&lt;UserRequest>()

    val createdUser = userService.save(
      user = userRequest.toModel()
    ) ?: return@post call.respond(HttpStatusCode.BadRequest)

    call.response.header(
      name = "id",
      value = createdUser.id.toString()
    )
    call.respond(
      message = HttpStatusCode.Created
    )
  }

  authenticate {
    get {
      val users = userService.findAll()

      call.respond(
        message = users.map(User::toResponse)
      )
    }
  }

  authenticate("another-auth") {
    get("/{id}") {
      val id: String = call.parameters["id"]
        ?: return@get call.respond(HttpStatusCode.BadRequest)

      val foundUser = userService.findById(id)
        ?: return@get call.respond(HttpStatusCode.NotFound)

      if (foundUser.username != extractPrincipalUsername(call))
        return@get call.respond(HttpStatusCode.NotFound)

      call.respond(
        message = foundUser.toResponse()
      )
    }
  }
}

private fun UserRequest.toModel(): User =
  User(
    id = UUID.randomUUID(),
    username = this.username,
    password = this.password
  )

private fun User.toResponse(): UserResponse =
  UserResponse(
    id = this.id,
    username = this.username,
  )

private fun extractPrincipalUsername(call: ApplicationCall): String? =
  call.principal&lt;JWTPrincipal>()
    ?.payload
    ?.getClaim("username")
    ?.asString()
</pre>



<p>As we can see, after our changes, every <code>GET /api/user</code> request will be verified using the provider, which we specified without the name. </p>



<p>When we check logs, we should even see the confirmation: </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>Matched routes:<br>&#8220;&#8221; -&gt; &#8220;api&#8221; -&gt; &#8220;user&#8221; -&gt; &#8220;(authenticate &#8220;default&#8221;)&#8221; -&gt; &#8220;(method:GET)&#8221;</cite></blockquote>



<p>On the other hand, every request to <code>GET /api/user/{id}</code> will be handled by the <code>another-auth</code> provider: </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>&#8220;&#8221; -&gt; &#8220;api&#8221; -&gt; &#8220;user&#8221; -&gt; &#8220;(authenticate another-auth)&#8221; -&gt; &#8220;{id}&#8221; -&gt; &#8220;(method:GET)&#8221;</cite></blockquote>



<p>Lastly, it&#8217;s worth mentioning that we changed one more thing: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">authenticate("another-auth") {
  get("/{id}") {
    val id: String = call.parameters["id"]
      ?: return@get call.respond(HttpStatusCode.BadRequest)

    val foundUser = userService.findById(id)
      ?: return@get call.respond(HttpStatusCode.NotFound)

    if (foundUser.username != extractPrincipalUsername(call))
      return@get call.respond(HttpStatusCode.NotFound)

    call.respond(
      message = foundUser.toResponse()
    )
  }
}


private fun extractPrincipalUsername(call: ApplicationCall): String? =
  call.principal&lt;JWTPrincipal>()
    ?.payload
    ?.getClaim("username")
    ?.asString()</pre>



<p>In this handler, we obtain the <code>JWTPrincipal</code> (created inside the JwtService) in order to read the <code>username</code>. If it matches with the found user, then we simply return the user response. But if not, then we return <code>404 Not Found</code>. </p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>And that&#8217;s all for this tutorial on how to implement a <strong>REST API with Ktor and Kotlin</strong> and how to secure it with <strong>JWT access tokens</strong>.</p>



<p>If you would like to find the whole source code, then check out <a href="https://github.com/codersee-blog/kotlin-ktor-jwt-access-tokens-authentication" target="_blank" rel="noreferrer noopener">this GitHub repository</a>.</p>



<p>Lastly, if you enjoyed this tutorial, then please do not forget to share it with other people on your social media (it will help me a lot) and check out the continuation for this post:</p>



<ul class="wp-block-list">
<li>h<a href="https://blog.codersee.com/ktor-app-with-jwt-refresh-token-flow/" target="_blank" rel="noreferrer noopener">ow to implement a <strong>refresh token flow </strong>with Ktor</a>,</li>



<li>and <a href="https://blog.codersee.com/secure-rest-api-ktor-role-based-authorization-rbac/" target="_blank" rel="noreferrer noopener">how to add a<strong> role-based access control (RBAC)</strong> to the project</a>. </li>
</ul>
<p>The post <a href="https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/">Secure REST API with Ktor and JWT Access Tokens</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/secure-rest-api-with-ktor-jwt-access-tokens/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>A Guide To Ktor With MongoDB</title>
		<link>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/</link>
					<comments>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 15 Mar 2022 06:15:29 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[KMongo]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2002634</guid>

					<description><![CDATA[<p>In this step by step guide, I would like to show you how to implement a REST API using Ktor, MongoDB and KMongo.</p>
<p>The post <a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">A Guide To Ktor With MongoDB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this step-by-step guide, I would like to show you how to implement a REST API using Ktor, MongoDB,<strong> and KMongo</strong>.</p>



<p>If you haven&#8217;t heard about <strong>KMongo</strong> yet- it is a lightweight Kotlin toolkit for Mongo. Internally, it uses the core MongoDB Java Driver and exposes its features via Kotlin extensions. With this combination, we are sure that we&#8217;re using the recommended Java Driver, and additionally, we take advantage of the Kotlin less-verbose syntax.</p>



<p>In the end, I would like to add one more note. In this article, I will show you how to create MongoDB as a Docker container. If you have MongoDB already installed on your local machine, then you can just skip this part. Otherwise, please make sure that you have configured Docker properly in your local environment.</p>



<h2 class="wp-block-heading" id="h-2-generate-project">2. Generate Project</h2>



<p>With that being said, let&#8217;s head to the <a href="https://start.ktor.io" target="_blank" rel="noopener">Ktor Project Generator page</a> and make a few adjustments.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Alternatively, you can generate the project within the app if you use IntelliJ IDEA Ultimate Edition</p>
</blockquote>



<h3 class="wp-block-heading" id="h-2-1-adjust-ktor-settings">2.1. Adjust Ktor Settings</h3>



<p>As the first step, let&#8217;s make sure that we set correctly the required properties:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="255" height="300" src="http://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-1-project-settings-255x300.png" alt="A screenshot from Ktor Project Generator page showing correctly adjustem project settings" class="wp-image-2002635" srcset="https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-1-project-settings-255x300.png 255w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-1-project-settings.png 722w" sizes="auto, (max-width: 255px) 100vw, 255px" /></figure>



<p>Basically, the Project Name, Website, and Artifact are irrelevant and you can adjust it as you want. Nevertheless, to make sure that everything will be working as intended, please make sure to select the same Ktor version on this page.</p>



<h3 class="wp-block-heading" id="h-2-2-add-ktor-plugins">2.2. Add Ktor Plugins</h3>



<p>Nextly, let&#8217;s click the Plugins button, and add <strong>kotlinx.serialization</strong> to our project:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="604" height="834" src="http://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-2-add-plugins.png" alt="A screenshot from showing how to add kotlinx serialization in the next step" class="wp-image-2002639" srcset="https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-2-add-plugins.png 604w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-2-add-plugins-217x300.png 217w" sizes="auto, (max-width: 604px) 100vw, 604px" /></figure>



<p>As we can see, two more plugins have been automatically: <strong>ContentNegotiation</strong> and <strong>Routing</strong>:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="600" height="757" src="http://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-3-additional-plugins.png" alt="A screenshot from showing two plugins added automatically: Routing and ContentNegotiation added automatically" class="wp-image-2002641" srcset="https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-3-additional-plugins.png 600w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-3-additional-plugins-238x300.png 238w" sizes="auto, (max-width: 600px) 100vw, 600px" /></figure>



<p>Unfortunately, we don&#8217;t have the possibility to add any dependencies required to work with MongoDB and Ktor, so for now, let&#8217;s click <em>Generate Project</em> button and import it to our IDE.</p>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-3-add-kmongo-dependency">3. Add KMongo Dependency</h2>



<p>After we import the project, let&#8217;s head to the <strong>gradle.properties</strong> file and specify the version of KMongo we&#8217;d like to use:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">kmongo_version=4.5.0</pre>



<p>Following, let&#8217;s open the<strong> build.gradle.kts</strong> file and put the following lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val kmongo_version: String by project

// Inside the dependencies
implementation("org.litote.kmongo:kmongo:$kmongo_version")</pre>



<h2 class="wp-block-heading" id="h-4-setup-mongodb-container">4. Setup MongoDB Container</h2>



<p>With all of the above being done, let&#8217;s prepare a MongoDB instance with Docker. Let&#8217;s open up the terminal and specify the following command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker run --name my_mongodb_cotainer -d -p 27017:27017 mongo:5.0.6</pre>



<p>Basically, this with this one-liner we perform a few actions:</p>



<ul class="wp-block-list">
<li>with &#8211;name [name] we assign our custom name to the container. Otherwise, a random one will be generated</li>



<li>-d, which is a shortcut for &#8211;detach, runs the container in the background and prints the container ID</li>



<li>with -p host_port:container_port, we simply publish the 27017 port of the local machine to the same port of Mongo. In simple words, it is necessary to connect using <strong>localhost:27017</strong> address</li>



<li>in the end, we specify the image we would like to use- 5.0.6 in our case</li>
</ul>



<p>To validate if everything is running as expected, let&#8217;s run the <em>docker ps</em> command and check the output:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker run --name my_mongodb_cotainer -d -p 27017:27017 mongo:5.0.6
CONTAINER ID  IMAGE        COMMAND                 CREATED         STATUS         PORTS                     NAMES
3b4369dd37c6  mongo:5.0.6  "docker-entrypoint.s…"  11 minutes ago  Up 11 minutes  0.0.0.0:27017->27017/tcp  my_mongodb_cotainer</pre>



<h2 class="wp-block-heading" id="h-5-ktor-configuration">5. Ktor Configuration</h2>



<p>If we made sure that the MongoDB instance is running, we can get back to our Ktor project and take a look at the configuration.</p>



<p>At this point, our project should consist of three files generated automatically: Application.kt, along with Routing.kt and Serialization.kt inside the <em>plugins</em> package. Please make sure that they are looking, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// Application.kt file:
fun main() {
  embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
    configureRouting()
    configureSerialization()
  }.start(wait = true)
}

// Routing.kt file:
fun Application.configureRouting() {
  routing {
  }
}

// Serialization.kt file:
fun Application.configureSerialization() {
  install(ContentNegotiation) {
    json()
  }
}

</pre>



<p>To put it simply, these three files are responsible for setting up our server to respond on <em>localhost:8080</em> and use <em>kotlinx.serialization</em>. Additionally, the <em>Routing.kt</em> is a skeleton, which we will use later to expose our endpoints.</p>



<h2 class="wp-block-heading" id="h-6-prepare-object-mapping">6. Prepare Object Mapping</h2>



<p>As the next step, let&#8217;s create a <strong>Person</strong> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">data class Person(
  @BsonId
  val id: Id&lt;Person>? = null,
  val name: String,
  val age: Int
)
</pre>



<p>As you can see, this simple class except for standard fields consists of the nullable id field annotated with @BsonId. Moreover, we need to use a parametrized Id&lt;T> interface in order to let KMongo know that the Person class is the owner of the given ID. By making this field explicitly null, the id will be generated automatically, so that we won&#8217;t have to pass it explicitly on creation.</p>



<h2 class="wp-block-heading" id="h-7-add-persondto">7. Add <strong>PersonDto</strong></h2>



<p>Following, let&#8217;s add a <strong>PersonDto</strong> to our project:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class PersonDto(
  val id: String? = null,
  val name: String,
  val age: Int
)
</pre>



<p>Although it&#8217;s not necessary, this way we will separate objects used to communicate between client and our Ktor app from the ones used to communicate with MongoDB.</p>



<p>Additionally, let&#8217;s create a <strong>PersonExtension.kt</strong> file with two extension functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Person.toDto(): PersonDto =
  PersonDto(
    id = this.id.toString(),
    name = this.name,
    age = this.age
  )

fun PersonDto.toPerson(): Person =
  Person(
    name = this.name,
    age = this.age
  )
</pre>



<p>These helper functions will serve us as mappers later in the code.</p>



<h2 class="wp-block-heading" id="h-8-implement-error-response">8. Implement Error Response</h2>



<p>As the last step of preparation, let&#8217;s create the ErrorResponse class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class ErrorResponse(val message: String) {
  companion object {
    val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
    val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
  }
}
</pre>



<p>Again, this is not necessary but will be helpful when informing clients about errors.</p>



<h2 class="wp-block-heading" id="h-9-establish-mongodb-connection">9. Establish MongoDB Connection</h2>



<p>With all the above being done, let&#8217;s create a <em>PersonService</em>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class PersonService {

  private val client = KMongo.createClient()
  private val database = client.getDatabase("person")
  private val personCollection = database.getCollection&lt;Person>()

}
</pre>



<p>Let&#8217;s have a look at each of the above lines:</p>



<ul class="wp-block-list">
<li><em>line 3- </em>creates a Mongo instance using localhost and the default port (27017). If we would like to change connection details, then we can pass it as String, or ConnectionString to its overloaded versions</li>



<li><em>line 4-</em> obtains a Databse instance by the given name. If it does not exist, it will be created during the first insert</li>



<li><em>line 5-</em> gets a Collection (a Table in Mongo terminology). We will use this object to perform all operations</li>
</ul>



<h2 class="wp-block-heading" id="h-10-insert-document-to-mongodb">10. Insert Document To MongoDB</h2>



<p>Following, let&#8217;s get to the heart of this tutorial- Ktor and MongoDB integration.</p>



<h3 class="wp-block-heading" id="h-10-1-add-create-function-to-service">10.1. Add Create Function To Service</h3>



<p>Let&#8217;s start by adding the <em>create()</em> function inside the <strong>PersonService</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun create(person: Person): Id&lt;Person>? {
  personCollection.insertOne(person)
  return person.id
}
</pre>



<p>As we can see, we use an already obtained <strong>MongoCollection</strong> instance to insert a provided Person object. If it is persisted successfully, then the ID will be updated with the value from the database, so that we can return it.</p>



<h3 class="wp-block-heading" id="h-10-2-expose-post-endpoint">10.2. Expose POST Endpoint</h3>



<p>As the next step, let&#8217;s head to the <strong>Routing.kt</strong> and insert the following lines:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val service = PersonService()

routing {
  route("/person") {
    post {
      val request = call.receive&lt;PersonDto>()
      val person = request.toPerson()

      service.create(person)
        ?.let { userId ->
          call.response.headers.append("My-User-Id-Header", userId.toString())
          call.respond(HttpStatusCode.Created)
        } ?: call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE)
    }
  }
}
</pre>



<p>Basically, the above logic is pretty straightforward. In the beginning, we obtain the JSON content of the request, which is deserialized to PersonDto instance. Then, we use our extension function to convert it to the Person class. If the document is persisted successfully, we simply return its id to the client setting the <strong>My-User-Id-Header</strong> and <strong>201 Created</strong> status code. On the other hand, if the id is null, we then return an error response along with <strong>400 Bad Request</strong> status.</p>



<h3 class="wp-block-heading" id="h-10-3-test-post-endpoint">10.3. Test POST Endpoint</h3>



<p>Nextly, let&#8217;s run our application and perform a <strong>POST</strong> request:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">POST http://localhost:8080/person

Payload:
{
  "name": "Piotr",
  "age": 55
}

Response Status: 
201 Created

Response Headers:
My-User-Id-Header: 6229a38236d4dc3abf0f3174 
</pre>



<p>So far, we don&#8217;t have endpoints to fetch the data, so let&#8217;s use <strong>MongoDB Compass</strong> to check.</p>



<h3 class="wp-block-heading" id="h-10-4-validate-with-mongodb-compass">10.4. Validate With MongoDB Compass</h3>



<p>Let&#8217;s open up the application and simply click Connect:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="632" height="335" src="http://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-4-compass-create-connection.png" alt="A screenshot from MongoDB Compass showing connection details form" class="wp-image-2002644" srcset="https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-4-compass-create-connection.png 632w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-4-compass-create-connection-300x159.png 300w" sizes="auto, (max-width: 632px) 100vw, 632px" /></figure>



<p>Following, select the person database and collection in the left panel:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="900" height="452" src="http://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-5-compass-person-collection.png" alt="A screenshot from MongoDB Compass showing inserted document to the person collection" class="wp-image-2002645" srcset="https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-5-compass-person-collection.png 900w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-5-compass-person-collection-300x151.png 300w, https://blog.codersee.com/wp-content/uploads/2022/03/ktor-mongodb-kmongo-5-compass-person-collection-768x386.png 768w" sizes="auto, (max-width: 900px) 100vw, 900px" /></figure>



<p>As we can see, the document was inserted correctly, which means that our Ktor setup works, as expected.</p>



<h2 class="wp-block-heading" id="h-11-obtain-documents-from-mongodb">11. Obtain Documents From MongoDB</h2>



<p>Nextly, let&#8217;s check how can we obtain documents from MongoDB using KMongo.</p>



<h3 class="wp-block-heading" id="h-11-1-add-findall-function">11.1. Add findAll() function</h3>



<p>Let&#8217;s get back to the <strong>PersonService</strong> and implement the <em>findAll()</em> function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun findAll(): List&lt;Person> =
  personCollection.find()
    .toList()</pre>



<p>It&#8217;s a really simple function and later I will show you how to enhance it with additional filters.</p>



<h3 class="wp-block-heading" id="h-11-2-expose-get-endpoint">11.2. Expose GET Endpoint</h3>



<p>For now, let&#8217;s expose a simple GET endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">get {
  val peopleList =
      service.findAll()
        .map(Person::toDto)

  call.respond(peopleList)
}</pre>



<p>The above code simply uses our newly created function and maps each <em>Person</em> object using its extension method <em>toDto()</em>.</p>



<h3 class="wp-block-heading" id="h-11-3-test-get-handler">11.3. Test GET Handler</h3>



<p>Finally, let&#8217;s perform a GET request to see if everything is OK:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">GET http://localhost:8080/person 

Response Status: 200 OK

Response Payload: 
[
  {
    "id": "6229a38236d4dc3abf0f3174",
    "name": "Piotr",
    "age": 55
  }
]</pre>



<p>As we can see, the previously inserted MongoDB document is returned, so we can assume this part is done.</p>



<h2 class="wp-block-heading" id="h-12-get-document-by-id">12. Get Document By ID</h2>



<p>As the next step, let&#8217;s add the handler responsible for fetching people by their ID.</p>



<h3 class="wp-block-heading" id="h-12-1-implement-findbyid-function">12.1. Implement findById() function</h3>



<p>Let&#8217;s add the <em>findById()</em> within our service:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun findById(id: String): Person? {
  val bsonId: Id&lt;Person> = ObjectId(id).toId()
  return personCollection
    .findOne(Person::id eq bsonId)
}</pre>



<p>One of the key features of KMongo is a <strong>type-safe query framework</strong>. As we can see above, to make sure that the appropriate argument type is passed to our database, we use a Kotlin property reference. This way, the code won&#8217;t compile if the input type won&#8217;t match the required one. In the next chapter, we will take a deeper look at this topic.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Alternatively, we can use .findOneById() and pass an ObjectId instance to it. Nevertheless, for the learning purpose I&#8217;ve deided to show you this approach, as well.</p>
</blockquote>



<h3 class="wp-block-heading" id="h-12-2-create-get-by-id-handler">12.2. Create GET By ID Handler</h3>



<p>For now, let&#8217;s insert a new handler to our codebase:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">get("/{id}") {
  val id = call.parameters["id"].toString()

  service.findById(id)
    ?.let { foundPerson -> call.respond(foundPerson.toDto()) }
    ?: call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE)
}</pre>



<p>As we can see- firstly, we obtain the identifier from the request path. If the person with the given ID exists in MongoDB, then <strong>we return PersonDto to our Ktor app client</strong>. In other cases, we simply return <strong>404 Not Found</strong> response.</p>



<h3 class="wp-block-heading" id="h-12-3-validate-get-by-id-endpoint">12.3. Validate GET By ID Endpoint</h3>



<p>Again, let&#8217;s make sure that the above theory meets practice:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">GET (existing ID) http://localhost:8080/person/6229a38236d4dc3abf0f3174

Response Status: 200 OK

Response Payload: 
{
  "id": "6229a38236d4dc3abf0f3174",
  "name": "Piotr",
  "age": 55
}

GET (non-existing ID) http://localhost:8080/person/6229a38236d4dc3abf0f3175

Response Status: 404 Not Found

Response Payload: 
{
  "message": "Person was not found"
}</pre>



<p>And again- that is the result we were expecting.</p>



<h2 class="wp-block-heading" id="h-13-search-with-additional-filters">13. Search With Additional Filters</h2>



<p>As the next step, let&#8217;s have a look at the KMongo filtering capabilities.</p>



<h3 class="wp-block-heading" id="h-13-1-create-type-safe-findbyname-method">13.1. Create Type-Safe findByName() method</h3>



<p>To do so, let&#8217;s add a <em>findByName()</em> function first:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun findByName(name: String): List&lt;Person> {
  val caseSensitiveTypeSafeFilter = Person::name regex name
  return personCollection.find(caseSensitiveTypeSafeFilter)
    .toList()
}</pre>



<p>To find any document containing a given string in Mongo, we can use leverage <strong>$regex</strong>. Of course, there are other possibilities, like a <em>$text</em> operator, but it does not have its type-safe version in KMongo.</p>



<p>Also, it&#8217;s worth mentioning that the above filter is <strong>case-sensitive.</strong></p>



<h3 class="wp-block-heading" id="h-13-2-add-case-insensitive-regex-filter">13.2. Add Case-Insensitive Regex Filter</h3>



<p>However, if we would like to search for the documents containing the given text regardless of the case, then we can use the overloaded regex function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val caseInsensitiveTypeSafeFilter = personCollection.find(
  (Person::name).regex(name, "i")
)</pre>



<p>As we can see, the above <em>regex</em> extension function allows us to specify additional options. The <strong>&#8220;i&#8221;</strong> flag simply instructs MongoDB to <strong>match both upper and lower cases</strong>.</p>



<p>And again- this approach allows us to make sure that the appropriate argument type is passed each time.</p>



<h3 class="wp-block-heading" id="h-13-3-pass-filter-as-a-string">13.3. Pass Filter As A String</h3>



<p>Although it is more error-prone, the <em>find()</em> function allows us also to specify filters as String values:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val nonTypeSafeFilter = personCollection.find(
  "{name:{'\$regex' : '$name', '\$options' : 'i'}}"
)</pre>



<p>In some cases, we don&#8217;t have any other possibility and in such a case we have to make sure to handle all the possible exceptions properly.</p>



<h3 class="wp-block-heading" id="h-13-4-and-operator">13.4. AND Operator</h3>



<p>Another useful feature is filter chaining. Let&#8217;s learn how to chain filters with the AND operator:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val withAndOperator = personCollection.find(
  and(Person::name regex name, Person::age gt 40)
)</pre>



<p>As we can see, the snippet from our Ktor app will look for all people persisted in MongoDB, which <strong>match the given name (case-sensitvie)</strong> and are <strong>older, than 40 years</strong>.</p>



<p>On the other hand, we can refactor the above code a bit:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val implicitAndOperator = personCollection.find(
  Person::name regex name, Person::age gt 40
)</pre>



<p>When we pass multiple filters to the <em>find()</em> function, they are<strong> implicitly combined with AND operator</strong>.</p>



<h3 class="wp-block-heading" id="h-13-5-or-operator">13.5. OR Operator</h3>



<p>Similarly, we can add the <strong>OR</strong> operator:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val withOrOperator = personCollection.find(
  or(Person::name regex name, Person::age gt 40)
)</pre>



<h3 class="wp-block-heading" id="h-13-6-add-ktor-search-handler">13.6. Add Ktor Search Handler</h3>



<p>To summarize the searching topic, let&#8217;s expose a new GET endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">get("/search") {
  val name = call.request.queryParameters["name"].toString()

  val foundPeople = service.findByName(name).map(Person::toDto)

  call.respond(foundPeople)
}</pre>



<p>This time, we use query parameters to obtain a name to look for. To put it simply, when searching for users with name John, the Ktor app client has to perform a <strong>GET localhost:8080/person/search?name=John</strong> request.</p>



<p>Given the fact I&#8217;ve presented various filters, I highly encourage you to test this endpoint on your own- by adding more data and validating each filter.</p>



<h2 class="wp-block-heading" id="h-14-update-document">14. Update Document</h2>



<p>As the second to last, let&#8217;s see how can we update the person by ID in our project.</p>



<h3 class="wp-block-heading" id="h-14-1-implement-updatebyid">14.1. Implement updateById()</h3>



<p>Firstly, let&#8217;s add this code to our service:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun updateById(id: String, request: Person): Boolean =
  findById(id)
    ?.let { person ->
      val updateResult = personCollection.replaceOne(person.copy(name = request.name, age = request.age))
      updateResult.modifiedCount == 1L
    } ?: false</pre>



<p>As we can see, firstly we make use of our existing findById function. As a result, we get either found person object, or null. Following we make use of <strong>replaceOne()</strong> function and as an argument we pass the same person instance with updated <em>name</em> and <em>age</em> fields.</p>



<p>As this function is supposed to update document by its unique identifier, it returns true <strong>only when exactly 1 item was modified</strong>.</p>



<h3 class="wp-block-heading" id="h-14-2-create-put-endpoint">14.2. Create PUT Endpoint</h3>



<p>As the next step, let&#8217;s expose a new endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">put("/{id}") {
  val id = call.parameters["id"].toString()
  val personRequest = call.receive&lt;PersonDto>()
  val person = personRequest.toPerson()

  val updatedSuccessfully = service.updateById(id, person)

  if (updatedSuccessfully) {
    call.respond(HttpStatusCode.NoContent)
  } else {
    call.respond(HttpStatusCode.BadRequest, ErrorResponse.BAD_REQUEST_RESPONSE)
  }
}</pre>



<p>This one combines together a few concepts we&#8217;ve learned already and as a result either <strong>204 No Content</strong>, or <strong>400 Bad Request</strong> will be returned.</p>



<h3 class="wp-block-heading" id="h-14-3-test-put-endpoint">14.3. Test PUT Endpoint</h3>



<p>And again, let&#8217;s see whether our endpoint is working correctly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">PUT (existing ID) http://localhost:8080/person/6229a38236d4dc3abf0f3174 

Request Payload: 
{ 
  "name": "Piotr- updated", 
  "age": 20
}

Response Status: 204 No Content

PUT (non-existing ID) http://localhost:8080/person/6229a38236d4dc3abf0f3173 

Request Payload: 
{ 
"name": "Piotr- updated", 
"age": 20
}

Response Status: 400 Bad Request

Response Payload:
{
  "message": "Invalid request"
}</pre>



<p>This time, to validate if the data were persisted correctly, we can either use existing GET by ID endpoint or check with MongoDB Compass.</p>



<h2 class="wp-block-heading" id="h-15-delete-document">15. Delete Document</h2>



<p>Finally, learn how to delete items from MongoDB in our Ktor project.</p>



<h3 class="wp-block-heading" id="h-15-1-create-deletebyid-function">15.1. Create deleteById() Function</h3>



<p>Let&#8217;s start by adding the deleteById() inside the service class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun deleteById(id: String): Boolean {
  val deleteResult = personCollection.deleteOneById(ObjectId(id))
  return deleteResult.deletedCount == 1L
}</pre>



<p>This time, we simply create a new ObjectId and pass it to <strong>deleteOneById()</strong> function. Similarly, the operation was successful only<strong> if the deleted count equals 1</strong>.</p>



<h3 class="wp-block-heading" id="h-15-2-add-delete-handler">15.2. Add DELETE Handler</h3>



<p>Following, let&#8217;s implement the DELETE endpoint handler:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">delete("/{id}") {
  val id = call.parameters["id"].toString()

  val deletedSuccessfully = service.deleteById(id)

  if (deletedSuccessfully) {
    call.respond(HttpStatusCode.NoContent)
  } else {
    call.respond(HttpStatusCode.NotFound, ErrorResponse.NOT_FOUND_RESPONSE)
  }
}</pre>



<p>This time, we simply invoke deleteById function using the String identifier passed by API client. If the Person has been deleted successfuly, we return <strong>204 No Content</strong> response. If that is not true, then we inform the client with <strong>404 status</strong>.</p>



<h3 class="wp-block-heading" id="h-15-3-validate-delete-endpoint">15.3. Validate DELETE Endpoint</h3>



<p>Finally, let&#8217;s see if everything was set up correctly:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">DELETE (existing ID) http://localhost:8080/person/6229a38236d4dc3abf0f3174 

Response Status: 204 No Content

DELETE (we can use the same ID) http://localhost:8080/person/6229a38236d4dc3abf0f3174

Response Status: 404 Not Found
Response Payload:
{
  "message": "Person was not found"
}</pre>



<p>As we can see, everything is working as expected.</p>



<h2 class="wp-block-heading" id="h-16-ktor-with-mongodb-and-kmongo-summary">16. Ktor With MongoDB and KMongo Summary</h2>



<p>And that would be all for this step-by-step guide on creating a CRUD REST API with Ktor, MongoDB,<strong> and KMongo</strong>.</p>



<p>I really hope that my materials will help you to learn new things and will be happy if you would like to share some feedback with me using the <a href="https://codersee.com/contact/"><strong>contact</strong></a> form.</p>



<p>If you would like to get the full source code for this project, please refer to this <a href="https://github.com/codersee-blog/ktor-mongodb-kmongo" target="_blank" rel="noopener"><strong>GitHub repository</strong></a>.</p>
<p>The post <a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">A Guide To Ktor With MongoDB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>REST API With Ktor, Ktorm and PostgreSQL</title>
		<link>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/</link>
					<comments>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Sun, 23 Jan 2022 07:00:52 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Ktorm]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=1002001</guid>

					<description><![CDATA[<p>In this step by step tutorial I will show you how to implement a simple REST API with Ktor, Ktorm and PostgreSQL.</p>
<p>The post <a href="https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/">REST API With Ktor, Ktorm and PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-1-introduction">1. Introduction</h2>



<p>In this step-by-step tutorial, I would like to show you how to create a simple <strong>REST API</strong> using <a href="https://ktor.io/" target="_blank" rel="noopener">Ktor</a>, <a href="https://www.ktorm.org/" target="_blank" rel="noopener">Ktorm</a>, and <a href="https://www.postgresql.org/" target="_blank" rel="noopener">PostgreSQL (aka Postgres)</a>.</p>



<p><strong>Ktor</strong> is a free, open-source framework, which allows us to easily build connected applications. When it comes to server-side applications, we can treat it as a lightweight and powerful alternative to Spring Boot. Additionally, it was written in Kotlin programming language by its&#8217; creators- the JetBrains team.</p>



<p><strong>Ktorm</strong>, on the other hand, is a lightweight and efficient <strong>ORM Framework for Kotlin</strong> directly based on pure JDBC. Furthermore, combined with <strong>PostgreSQL</strong> makes an interesting combination for a REST API tech stack.</p>



<h2 class="wp-block-heading" id="h-2-generate-ktor-project">2. Generate Ktor Project</h2>



<p>With all of that being said, let&#8217;s create the Ktor project (and in the next chapter, I will show you how to add Ktorm and PostgreSQL dependencies).</p>



<p>If you are the owner of <em>IntelliJ Ultimate Edition</em>, then it ships with Ktor support out of the box. Nevertheless, in this tutorial, we&#8217;ll focus on the alternative: <a href="https://start.ktor.io" target="_blank" rel="noopener">https://start.ktor.io</a>, which is pretty similar to the <em>Spring Initializr</em> page.</p>



<p>As the first step, let&#8217;s specify the project name along with other, necessary data:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="730" height="868" src="http://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1.png" alt="Image shows project generator page used to generate our ktor with ktorm and postgresql project" class="wp-image-1002002" srcset="https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1.png 730w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-1-252x300.png 252w" sizes="auto, (max-width: 730px) 100vw, 730px" /></figure>



<p>Please remember about selecting the same Ktor version, so that the code from this tutorial will work for sure. Don&#8217;t worry about a sample code generated, we will take care of it later.</p>



<p>As the next step, let&#8217;s click the<strong> Add plugins</strong> button and add the <strong>kotlinx.serialization</strong>&#8211; we&#8217;ll use it to serialize our objects into JSON format:</p>



<figure class="wp-block-image aligncenter"><img loading="lazy" decoding="async" width="646" height="642" src="http://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2.png" alt="Image shows ktor project generator page" class="wp-image-1002003" srcset="https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2.png 646w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2-300x298.png 300w, https://blog.codersee.com/wp-content/uploads/2022/01/ktor-ktorm-postgresql-2-150x150.png 150w" sizes="auto, (max-width: 646px) 100vw, 646px" /></figure>



<p>As we can see, two other plugins have been added automatically: <strong>ContentNegotiation</strong> and <strong>Routing</strong>. Please do not remove them, we will need them later.</p>


<p>[elementor-template id=&#8221;9007393&#8243;]</p>



<h2 class="wp-block-heading" id="h-2-add-ktorm-and-postgresql-to-ktor">2. Add Ktorm and PostgreSQL To Ktor</h2>



<p>Nextly, let&#8217;s open up our Ktor Postgres project with IntelliJ and go to the <strong>gradle.properties</strong> file.</p>



<p>Unfortunately, the Project Generator does not allow us to add all of the necessary imports, so we will have to do it manually:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">postgresql_driver_version=42.3.1
ktorm_version=3.4.1</pre>



<p>As the next step, let&#8217;s add a few lines within the <strong>build.gradle.kts</strong> file and <strong>reload the Gradle project</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val ktorm_version: String by project
val postgresql_driver_version: String by project

dependencies {

// other dependencies

implementation("org.ktorm:ktorm-core:$ktorm_version")
implementation("org.ktorm:ktorm-support-postgresql:$ktorm_version")
implementation("org.postgresql:postgresql:$postgresql_driver_version")

}</pre>



<p>As we can see, these dependencies are necessary in order to connect our Ktor application with the Postgres database.</p>



<h2 class="wp-block-heading" id="h-3-clean-up-the-code">3. Clean Up The Code</h2>



<p>After that, let&#8217;s clean up the project a bit and delete the <strong>Routing.kt</strong> along with its invocation within the <strong>Application.kt</strong>.</p>



<p>The second file should now be looking, as follows:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun main() {
  embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
    configureSerialization()
  }.start(wait = true)
}</pre>



<p>Then, let&#8217;s go to the <strong>Serialization.kt</strong> file and remove unnecessary code, as well:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Application.configureSerialization() {
  install(ContentNegotiation) {
    json()
  }
}</pre>



<p>The above code will be necessary for JSON serialization.</p>



<h2 class="wp-block-heading" id="h-4-prepare-postgresql-with-docker-compose">4. Prepare PostgreSQL With Docker Compose</h2>



<p>Basically, if you already have a PostgreSQL instance running on your machine, you can just skip the docker-compose part and just run the below SQL script.</p>



<p>Either way, let&#8217;s create the <strong>sql</strong> folder in the root directory and add the <strong>init-db.sql</strong> file:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="sql" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CREATE TABLE book
(
  id   SERIAL NOT NULL,
  name varchar NOT NULL
);

INSERT INTO book(name) VALUES ('Book #1');
INSERT INTO book(name) VALUES ('Book #2');</pre>



<p>We can clearly see, that after the script is run, a new table will be added and populated with test data.</p>



<p>Following, let&#8217;s go to the root of the Ktor project and add the <strong>docker-compose.yaml</strong> file with Postgres definition:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="yaml" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">version: "3.9"
services:
  postgres-sandbox:
    image: postgres:14
    ports:
      - '5438:5432'
    volumes:
      - ./sandbox-db:/var/lib/postgresql/data
      - ./sql/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    environment:
      - POSTGRES_NAME=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres</pre>



<p>The above code is responsible for a few things:</p>



<ul class="wp-block-list">
<li>pulling the Docker image for PostgreSQL</li>



<li>publishing the container&#8217;s <strong>5432</strong> port to the host&#8217;s <strong>5438 </strong>(to put it simply- we will connect from our application using port <strong>5438</strong>)</li>



<li>running initialization script prepared above</li>



<li>and finally, setting credentials through environment variables</li>
</ul>



<p>To start our PostgreSQL instance, let&#8217;s run the below command:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">docker-compose up

# To validate:
docker ps
CONTAINER ID IMAGE       COMMAND                CREATED       STATUS       PORTS                  NAMES
2f5716235039 postgres:14 "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:5438->5432/tcp ktor-ktorm-postgresql_postgres-sandbox_1

</pre>



<p>As we can see, everything is working fine and now we will be able to connect our Ktor app with Postgres.</p>



<h2 class="wp-block-heading" id="h-5-implement-dtos">5. Implement DTOs</h2>



<p>Following, let&#8217;s prepare request and response classes, which will be used in our REST API. Technically, we don&#8217;t have to do that, but it&#8217;s a good practice to keep them separate.</p>



<p>Let&#8217;s start with the <strong>BookRequest</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class BookRequest(
  val name: String
)</pre>



<p>Similarly, let&#8217;s implement the <strong>BookResponse </strong>and <strong>ErrorResponse</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Serializable
data class BookResponse(
  val id: Long,
  val name: String
)

@Serializable
data class ErrorResponse(val message: String)</pre>



<p>You might have noticed, that both classes are annotated with <strong>@Serializable</strong> (from <strong>kotlinx.serialization </strong>library). According to the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Applying Serializable to the Kotlin class instructs the serialization plugin to automatically generate implementation of KSerializer for the current class, that can be used to serialize and deserialize the class.</p>
</blockquote>



<p>To put it simply, this annotation is necessary if we want to operate on these two classes in our REST API.</p>



<h2 class="wp-block-heading" id="h-6-add-entity-and-columns-bindings">6. Add Entity And Columns Bindings</h2>



<p>With all of that being done, let&#8217;s add the code responsible for <strong>object-relational mapping</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">interface Book : Entity&lt;Book> {
  companion object : Entity.Factory&lt;Book>()

  val id: Long?
  var name: String
}

object Books : Table&lt;Book>("book") {
  val id = long("id").primaryKey().bindTo(Book::id)
  val name = varchar("name").bindTo(Book::name)
}</pre>



<p>It may seem a bit counterintuitive in the beginning, so let&#8217;s spend some time understanding it better.</p>



<p>The first one, <strong>Book</strong>, is a representation of the entity class, which will be used to operate on it. You might have already noticed, that it&#8217;s been declared as an interface- <strong>that’s a design requirement of Ktorm</strong>. (Ktorm 2.5 introduced the possibility to declare them as classes, but this approach has some limitations, which you can read more about<a href="https://www.ktorm.org/en/define-entities-as-any-kind-of-classes.html" target="_blank" rel="noopener"> here</a>).</p>



<p>Additionally, you might be curious, what exactly does this line do?</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">companion object : Entity.Factory&lt;Book>()
</pre>



<p>Basically, interfaces cannot be instantiated, but to help us with that, Ktorm provides an abstract class <strong>Entity.Factory</strong>. It overloads the <strong>invoke</strong> operator so that we will be able to create Book instances and initialize its properties pretty easily:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val newBook = Book {
  name = bookRequest.name
}
</pre>



<p>Finally, the second part, an object called <strong>Books</strong> is responsible for binding our entity with the database table.</p>



<p>It allows us to specify columns types, and names and wire them with our entity through the <strong>.bindTo()</strong> method. Whatsoever, we will use this object later, when working with our Ktor Postgres database connection.</p>



<h2 class="wp-block-heading" id="h-7-connect-to-postgresql">7. Connect To PostgreSQL</h2>



<p>As the next step, let&#8217;s implement the <strong>BookService</strong> and connect to the database:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class BookService {

  private val database = Database.connect(
    url = "jdbc:postgresql://localhost:5438/postgres",
    driver = "org.postgresql.Driver",
    user = "postgres",
    password = "postgres"
  )
}</pre>



<p>As we can see, the connect function allows us to specify Ktor Postgres connection details. Please make sure that these values match the actual state in your case. As a result, we receive a new database object, which we will use to perform DB operations.</p>



<h2 class="wp-block-heading" id="h-8-implement-service-methods">8. Implement Service Methods</h2>



<p>With all of that being done, we can finally add logic responsible for books management.</p>



<p>Let&#8217;s go to the <strong>BookService</strong> and add 4 methods:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createBook(bookRequest: BookRequest): Boolean {
  val newBook = Book {
    name = bookRequest.name
  }

  val affectedRecordsNumber = 
    database.sequenceOf(Books)
      .add(newBook)

  return affectedRecordsNumber == 1
}

fun findAllBooks(): Set&lt;Book> =
  database.sequenceOf(Books).toSet()

fun findBookById(bookId: Long): Book? =
  database.sequenceOf(Books)
    .find { book -> book.id eq bookId }

fun updateBookById(bookId: Long, bookRequest: BookRequest): Boolean {
  val foundBook = findBookById(bookId)
    foundBook?.name = bookRequest.name

  val affectedRecordsNumber = foundBook?.flushChanges()

  return affectedRecordsNumber == 1
}
fun deleteBookById(bookId: Long): Boolean {
  val foundBook = findBookById(bookId)

  val affectedRecordsNumber = foundBook?.delete()

  return affectedRecordsNumber == 1
}</pre>



<h3 class="wp-block-heading" id="h-8-1-create-book">8.1. Create Book</h3>



<p>Let&#8217;s take a while to analyze each method:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun createBook(bookRequest: BookRequest): Boolean {
  val newBook = Book {
    name = bookRequest.name
  }

  val affectedRecordsNumber = 
    database.sequenceOf(Books)
      .add(newBook)

  return affectedRecordsNumber == 1
}
</pre>



<p>As we can see, the <strong>createBook() </strong>creates a new Book instance (thanks to the companion object) and inserts it into the table using the <strong>database</strong> object.</p>



<p>The <strong>sequenceOf()</strong>, instantiates a new <strong>EntitySequence</strong> object. It exposes a sequence API, which we can use to perform DB operations, like querying or inserting data.</p>



<p>Finally, the <strong>.add()</strong> method inserts our entity into the database and returns the number of affected records.</p>



<p>The last line of this function simply validates, whether the number of affected records is equal to one (any other number indicates that something went wrong).</p>



<h3 class="wp-block-heading" id="h-8-2-fetching-books">8.2. Fetching Books</h3>



<p>Nextly, let&#8217;s check the querying functions:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun findAllBooks(): Set&lt;Book> =
  database.sequenceOf(Books).toSet()

fun findBookById(bookId: Long): Book? =
  database.sequenceOf(Books)
    .find { book -> book.id eq bookId }
</pre>



<p>They are pretty much the same, except for the fact, that in the second case the <strong>.find{}</strong> function will be translated to the WHERE clause in the generated SQL.</p>



<h3 class="wp-block-heading" id="h-8-3-update-book">8.3. Update Book</h3>



<p>As the next one, let&#8217;s check out the <strong>updateBookById()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun updateBookById(bookId: Long, bookRequest: BookRequest): Boolean {
  val foundBook = findBookById(bookId)
  foundBook?.name = bookRequest.name

  val affectedRecordsNumber = foundBook?.flushChanges()

  return affectedRecordsNumber == 1
}</pre>



<p>We can clearly see, that we can operate on the fetched entity, just like on any object in Kotlin. Nevertheless, for the changes to take effect, we must invoke the <strong>.flushChanges()</strong> function, which returns the affected records number.</p>



<h3 class="wp-block-heading" id="h-8-4-delete-book">8.4. Delete Book</h3>



<p>Finally, the <strong>deleteBookById</strong>():</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun deleteBookById(bookId: Long): Boolean {
  val foundBook = findBookById(bookId)

  val affectedRecordsNumber = foundBook?.delete()

  return affectedRecordsNumber == 1
}</pre>



<p>Although it&#8217;s performing a different action, the mechanism behind it remains the same.</p>



<h2 class="wp-block-heading" id="h-9-implement-book-routes">9. Implement Book Routes</h2>



<p>With all of that being done, we can finally add the necessary routes.</p>



<p>In Ktor, <strong>Routing</strong> is a core plugin responsible for handling incoming requests. If you&#8217;ve ever been working with Spring Boot before, then you may find it similar to the Controller concept.</p>



<h3 class="wp-block-heading" id="h-9-1-edit-application-kt">9.1. Edit Application.kt</h3>



<p>As the first step, let&#8217;s get back to the <strong>Application.kt</strong> and add <strong>configureBookRoutes()</strong> invocation:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun main() {
  embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
    configureBookRoutes()
    configureSerialization()
  }.start(wait = true)
}</pre>



<h3 class="wp-block-heading" id="h-9-2-create-bookroutes-kt">9.2. Create BookRoutes.kt</h3>



<p>Secondly, let&#8217;s add the <strong>BookRoutes.kt</strong> file with the necessary function:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Application.configureBookRoutes() {
  routing {
    route("/book") {
      val bookService = BookService()
      createBook(bookService)
      getAllBooksRoute(bookService)
      getBookByIdRoute(bookService)
      updateBookByIdRoute(bookService)
      deleteBookByIdRoute(bookService)
    }
  }
}

private fun Book?.toBookResponse(): BookResponse? =
  this?.let { BookResponse(it.id!!, it.name) }</pre>



<p>To put it simply, the <strong>routing</strong> block installs a Routing feature in our application. On the other hand, the <strong>route </strong>builds a route to match the specified path. This way, we can keep relevant functions together and avoid duplicating the <em>/book</em> path for each of them.</p>



<p>When it comes to the <strong>toBookResponse()</strong>, this extension function will help us to convert <strong>Book</strong> entities into <strong>BookResponses</strong>.</p>



<h3 class="wp-block-heading" id="h-9-3-add-create-book-route">9.3. Add Create Book Route</h3>



<p>With this in mind, let&#8217;s implement the rest of the code step by step. As the first one, let&#8217;s add the <strong>createBook()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.createBook(bookService: BookService) {
  post {
    val request = call.receive&lt;BookRequest>()

    val success = bookService.createBook(bookRequest = request)

    if (success)
      call.respond(HttpStatusCode.Created)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot create book"))
  }
}</pre>



<p>The above code will be responsible for handling <em>POST /book</em> requests. As a word of explanation, the <strong>call.receive&lt;BookRequest&gt;()</strong> deserializes the content of the request (JSON) into the BookRequest instance. When the content cannot be translated, it throws <strong>ContentTransformationException</strong> (it&#8217;s worth to remember in real-life scenarios).</p>



<p>On the contrary, the <strong><span class="enlighter-text"> call.</span><span class="enlighter-m3">respond</span>()</strong> is responsible for sending responses to the client. We can clearly see that if creation is successful, the <strong>201 Created</strong> is returned. However, if this is not the case, then the <strong>400 Bad Request</strong> with an appropriate message is returned.</p>



<h3 class="wp-block-heading" id="h-9-4-implement-get-all-books-route">9.4. Implement Get All Books Route</h3>



<p>As the next step, let&#8217;s add the <strong>getAllBooksRounte()</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.getAllBooksRoute(bookService: BookService) {
  get {
    val books = bookService.findAllBooks()
      .map(Book::toBookResponse)

    call.respond(message = books)
  }
}</pre>



<p>This one will be triggered on <em>GET /book</em> requests returning the list of books.</p>



<h3 class="wp-block-heading" id="h-9-5-add-get-book-by-id-route">9.5. Add Get Book By Id Route</h3>



<p>With that being done, let&#8217;s add the <strong>getBookByIdRoute()</strong> responsible for handling GET /book/{id} requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.getBookByIdRoute(bookService: BookService) {
  get("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@get call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    bookService.findBookById(id)
      ?.let { foundBook -> foundBook.toBookResponse() }
      ?.let { response -> call.respond(response) }
      ?: return@get call.respond(HttpStatusCode.BadRequest, ErrorResponse("Book with id [$id] not found"))
  }
}</pre>



<p>This time, let&#8217;s focus on the <strong>call.parameters[&#8220;id&#8221;]</strong>. The Parameters property of the call is simply a map of case-insensitive names to a collection of String values. That&#8217;s why we have to use a safe <strong>?.toLongOrNull()</strong> to obtain the value correctly. If the id is invalid, then the <strong>400 Bad Request</strong> is returned.</p>



<p>The rest of the code is pretty straightforward- either the found book is mapped to the <strong>BookResponse</strong> instance and returned to the client, or a <strong>400 Bad Reques</strong>t informing that the book with a given id was not found.</p>



<h3 class="wp-block-heading" id="h-9-6-insert-update-and-delete-routes">9.6. Insert Update and Delete Routes</h3>



<p>Finally, let&#8217;s add the two routes responsible for handling <em>PATCH /book/{id}</em> and <em>DELETE /book/{id}</em> requests:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Route.updateBookByIdRoute(bookService: BookService) {
  patch("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@patch call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    val request = call.receive&lt;BookRequest>()
    val success = bookService.updateBookById(id, request)

    if (success)
      call.respond(HttpStatusCode.NoContent)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot update book with id [$id]"))
  }
}

fun Route.deleteBookByIdRoute(bookService: BookService) {
  delete("/{id}") {
    val id: Long = call.parameters["id"]?.toLongOrNull()
      ?: return@delete call.respond(HttpStatusCode.BadRequest, ErrorResponse("Invalid id"))

    val success = bookService.deleteBookById(id)

    if (success)
      call.respond(HttpStatusCode.NoContent)
    else
      call.respond(HttpStatusCode.BadRequest, ErrorResponse("Cannot delete book with id [$id]"))
  }
}</pre>



<p>As we can see, these functions use already described concepts, so we won&#8217;t go into much detail.</p>



<h2 class="wp-block-heading" id="h-10-test-with-curl">10. Test With cURL</h2>



<p>Finally, let&#8217;s run the application and test it manually. For simplicity, we will use the <strong>cURL</strong> command. Alternatively, we could do that through Postman, or Insomnia.</p>



<h3 class="wp-block-heading" id="h-10-1-get-book">10.1. GET /book</h3>



<p>Let&#8217;s start by listing all books to validate both the endpoint and SQL init script:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl localhost:8080/book

# Output
[
  {
    "id":1,
    "name": "Book #1"
  },
  {
    "id":2,
    "name":"Book #2"
  }
]</pre>



<p>As we can see, everything is working correctly.</p>



<h3 class="wp-block-heading" id="h-10-2-post-book">10.2. POST /book</h3>



<p>As the second step, let&#8217;s create a new book:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X POST 'localhost:8080/book' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "name": "Book #3"
  }'

# Then again: 
curl localhost:8080/book

# Output
[
  {
    "id":1,
    "name": "Book #1"
  },
  {
    "id":2,
    "name":"Book #2"
  },
  {
    "id":3,
    "name":"Book #3"
  }
]</pre>



<h3 class="wp-block-heading" id="h-10-3-get-book-id">10.3. GET /book/{id}</h3>



<p>As the third step, let&#8217;s check whether fetching book details works:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl localhost:8080/book/3

# Output:
{
  "id":3,
  "name":"Book #3"
}

curl localhost:8080/book/1234

# Output:
{
  "message":"Book with id [1234] not found"}
}</pre>



<h3 class="wp-block-heading" id="h-10-4-patch-book-id">10.4. PATCH /book/{id}</h3>



<p>After that, let&#8217;s validate the PATCH endpoint:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X PATCH 'localhost:8080/book/3' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "name": "Book #3- edited"
  }'

# Then, let's check with GET:
curl localhost:8080/book/3

# Output:
{
  "id": 3,
  "name": "Book #3- edited"
}</pre>



<h3 class="wp-block-heading" id="h-10-5-delete-book-id">10.5. DELETE /book/{id}</h3>



<p>Finally, let&#8217;s delete created book:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl -X DELETE 'localhost:8080/book/3'

# Then, let's check with GET:
curl localhost:8080/book/3

# Output:
{
  "message":"Book with id [3] not found"
}</pre>



<h2 class="wp-block-heading" id="h-11-ktor-with-ktorm-and-postgresql-summary">11. Ktor With Ktorm And PostgreSQL Summary</h2>



<p>And that would be all for this step-by-step guide on <strong>how to create REST API with Ktor, Ktorm, and PostgreSQL</strong>. I really hope you will find this material useful when working with <strong>Ktor</strong>. If you would like to see the source code, please refer to <a href="https://github.com/codersee-blog/ktor-ktorm-postgresql" target="_blank" rel="noopener">this GitHub repository</a>.</p>



<p>If you find this one useful, then you may enjoy my newer article about <a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">Ktor and MongoDB</a>.</p>



<p>As always- if you would like to ask me about anything, please do that in the comments section below, or through the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>.</p>



<h4 class="wp-block-heading" id="h-previous-articles-you-might-be-interested-in">Previous articles, you might be interested in:</h4>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/exception-handling-with-restcontrolleradvice-and-exceptionhandler/" target="_blank" rel="noopener">Exception Handling with @RestControllerAdvice and @ExceptionHandler</a></li>



<li><a href="https://blog.codersee.com/16-intellij-shortcuts-that-will-boost-your-productivity/" target="_blank" rel="noopener">16 IntelliJ Shortcuts That Will Boost Your Productivity</a></li>



<li><a href="https://blog.codersee.com/solid-principles-with-kotlin-examples/" target="_blank" rel="noopener">SOLID Principles with Kotlin Examples</a></li>
</ul>
<p>The post <a href="https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/">REST API With Ktor, Ktorm and PostgreSQL</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/rest-api-ktor-ktorm-postgresql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-02-24 04:50:06 by W3 Total Cache
-->