<?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>Kotlin DSL Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/kotlin-dsl/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:49:52 +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>Kotlin DSL Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Scope Control With @DslMarker Annotation. Kotlin DSLs.</title>
		<link>https://blog.codersee.com/scope-control-dslmarker-kotlin-dsl/</link>
					<comments>https://blog.codersee.com/scope-control-dslmarker-kotlin-dsl/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 07 Nov 2023 13:35:19 +0000</pubDate>
				<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Annotations]]></category>
		<category><![CDATA[Core Kotlin]]></category>
		<category><![CDATA[Kotlin DSL]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9008328</guid>

					<description><![CDATA[<p>This time, I will show you how to control scope with @DslMarker annotation when creating your DSLs in Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/scope-control-dslmarker-kotlin-dsl/">Scope Control With @DslMarker Annotation. Kotlin DSLs.</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 next lesson! This time, I will show you how to <strong>control scope with @DslMarker</strong> annotation when creating your DSLs in Kotlin. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>Note: this article is based on my Complete <a href="https://codersee.com/the-complete-kotlin-course/">Kotlin Course</a> lesson, which I highly encourage you to check out if you are looking for a comprehensive Kotlin guide. </cite></blockquote>



<p>If this is your first meeting with DSLs, then I would recommend you to check out my other article, in which I explain and show how to <a href="https://blog.codersee.com/kotlin-type-safe-builders-make-your-custom-dsl/">implement Kotlin DSL</a> step-by-step.</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/scope-control-dslmarker-kotlin-dsl/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FuItQGNnbUXo%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-uncontrolled-scope">Uncontrolled Scope</h2>



<p>But before we dive into the solution with Kotlin @DslMarker, let&#8217;s understand what problem it solves first. </p>



<p>So as the first step, let&#8217;s prepare a simple DSL with <code>Board</code>, <code>Task</code>, <code>Author</code>, and <code>Comment</code> classes:</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="">enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
  val tasks: MutableList&lt;Task> = mutableListOf()

  fun task(init: Task.() -> Unit) {
    val task = Task().apply(init)
    tasks.add(task)
  }
}

class Task {
  var title: String = ""
  var description: String = ""
  val comments: MutableList&lt;Comment> = mutableListOf()

  fun comment(init: Comment.() -> Unit) {
    val comment = Comment().apply(init)
    comments.add(comment)
  }
}

class Comment {
  var comment: String = ""
  var author: Author = Author()

  fun author(init: Author.() -> Unit) {
    val author = Author().apply(init)
    this.author = author
  }
}

class Author {
  var name: String = ""
}

fun board(init: Board.() -> Unit): Board =
  Board()
    .apply(init)</pre>



<p>With the following Kotlin DSL implementation, we would expect that we could introduce different Boards, with Tasks inside them, which would have some Comments written by Authors. </p>



<p>But let&#8217;s take a look at the following 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="">fun main() {
  val board = board {
    task {
      task {
        task {
          comment {
            task { }
            author {
              task { }
              comment {
                task { }
              }
            }
          }
        }
      }
    }
  }
}</pre>



<p>Will it compile? <strong>Unfortunately, yes</strong>.</p>



<p>We invoke a <em>task</em> within a <em>task</em>, within a <em>task</em>, and so on. Moreover, we can invoke the <em>task</em>, or <em>comment</em> functions inside the <em>author</em>.&nbsp;</p>



<p>And that&#8217;s because, <strong>by default, we can call the methods of every available receive</strong>r, which can lead to such situations.&nbsp;</p>



<h2 class="wp-block-heading" id="h-kotlin-dslmarker-to-the-rescue">Kotlin @DslMarker To The Rescue</h2>



<p>At this point, we already know what the issue is and what we will use to fix it. </p>



<p>But what exactly does the @DslMarker do in Kotlin? </p>



<p>Well, in practice, we use this annotation to introduce our new custom annotations. Then, we make use of them to mark classes and receivers, thus preventing receivers marked with the same annotation from being accessed inside one another.</p>



<p>This way, we won’t be able to access members of the outer receiver (like the&nbsp;<em>task&nbsp;</em>function, which is declared inside the&nbsp;<em>Board</em> class from the <em>Task</em> class instances).</p>



<p>Let&#8217;s consider the updated example: </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="">@DslMarker
annotation class BoardDsl

enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

@BoardDsl
class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
  val tasks: MutableList&lt;Task> = mutableListOf()

  fun task(init: Task.() -> Unit) {
    val task = Task().apply(init)
    tasks.add(task)
  }
}

@BoardDsl
class Task {
  var title: String = ""
  var description: String = ""
  val comments: MutableList&lt;Comment> = mutableListOf()

  fun comment(init: Comment.() -> Unit) {
    val comment = Comment().apply(init)
    comments.add(comment)
  }
}

@BoardDsl
class Comment {
  var comment: String = ""
  var author: Author = Author()

  fun author(init: Author.() -> Unit) {
    val author = Author().apply(init)
    this.author = author
  }
}

@BoardDsl
class Author {
  var name: String = ""
}

fun board(init: Board.() -> Unit): Board =
  Board()
    .apply(init)

fun main() {
  val board = board {
    task {  // OK
      task {  // Does not compile
        task {  // Does not compile
          comment {  // OK
            task { }  // Does not compile
            author {  // OK
              task { }  // Does not compile
              comment {  // Does not compile
                task { }  // Does not compile
              }
            }
          }
        }
      }
    }
  }
}</pre>



<p>Firstly, we introduce the <code>@BoardDsl</code> annotation, which uses the <code>@DslMarker</code>. </p>



<p>Following, we must annotate every class in our hierarchy with our new annotation- this way, we make the Kotlin compiler &#8220;aware&#8221; of the hierarchy in our DSL. </p>



<p>Lastly, we can clearly see that the code <strong>will not compile</strong> whenever we try to nest the unwanted type inside another. </p>



<h2 class="wp-block-heading" id="h-scope-control-with-kotlin-dslmarker-summary">Scope Control With Kotlin @DslMarker Summary</h2>



<p>Basically, that&#8217;s all for this tutorial on <strong>how to control scope</strong> when implementing a <strong>Kotlin DSL</strong> using the <strong>@DslMarker </strong>summary.</p>



<p>If you enjoy such a short, practice-focused learning approach then do not forget to check out my course and let me know in the comments section. </p>



<p>If you&#8217;d like to learn a bit more about the annotation itself, then the <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-dsl-marker/" target="_blank" rel="noreferrer noopener">documentation</a> may be useful to you too. </p>
<p>The post <a href="https://blog.codersee.com/scope-control-dslmarker-kotlin-dsl/">Scope Control With @DslMarker Annotation. Kotlin DSLs.</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/scope-control-dslmarker-kotlin-dsl/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Kotlin Type-Safe Builders Explained. Implement Your Own DSL.</title>
		<link>https://blog.codersee.com/kotlin-type-safe-builders-make-your-custom-dsl/</link>
					<comments>https://blog.codersee.com/kotlin-type-safe-builders-make-your-custom-dsl/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Wed, 17 May 2023 08:18:54 +0000</pubDate>
				<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Core Kotlin]]></category>
		<category><![CDATA[Kotlin DSL]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9007512</guid>

					<description><![CDATA[<p>In this publication, we will learn how to implement statically-typed, type-safe Kotlin builders which we can use to implement our own DSLs.</p>
<p>The post <a href="https://blog.codersee.com/kotlin-type-safe-builders-make-your-custom-dsl/">Kotlin Type-Safe Builders Explained. Implement Your Own DSL.</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>Hi! 🙂 In this publication, we will learn how to implement <strong>statically-typed</strong>, <strong>type-safe Kotlin builders</strong> which we can use to implement our own DSLs. </p>



<p>Firstly, we will discuss what exactly DSLs are, their benefits, and why Kotlin is a good choice for creating them. Then, we will learn more about function literals with a receiver. And finally, we will put together all this knowledge and implement our own DSL. <br></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Note:</strong> This article has been created based on my course <a href="http://codersee.com/kotlin-handbook-learn-through-practice-course/" target="_blank" rel="noreferrer noopener"><em>Kotlin Handbook. Learn Through Practice</em></a>. If you are looking for high-quality, step-by-step Kotlin learning path, then you should definitely check it out.</p>
</blockquote>



<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/kotlin-type-safe-builders-make-your-custom-dsl/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2Fg4ioA_LcBWE%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-2-what-is-a-dsl">2. What is a DSL?</h2>



<p>A DSL is an acronym for Domain-Specific Language.</p>



<p>It’s nothing else than a specialized language designed to solve problems or tasks within a specific domain.</p>



<p>Unlike general-purpose languages (like Kotlin itself), which are designed for a wide range of applications, DSLs focus on easy-to-understand syntax tailored to a particular problem domain.</p>



<p>Great examples can be:</p>



<ul class="wp-block-list">
<li>HTML for creating web pages, </li>



<li>Gradle for building automation, </li>



<li>or SQL for querying databases.</li>
</ul>



<h2 class="wp-block-heading" id="h-3-the-purpose-and-benefits-of-dsls">3. The Purpose and Benefits of DSLs</h2>



<p>The main purpose of DSLs is to help programmers to deal with complex hierarchical data structures in an easy way.&nbsp;</p>



<p>So, a well-designed domain-specific language provides (among others) the following benefits:&nbsp;</p>



<ul class="wp-block-list">
<li><strong>expressiveness</strong>&#8211; helps us to express complex ideas and logic using a more concise and natural syntax,</li>



<li><strong>readability</strong>&#8211; the code we produce is simply easier to read and understand,&nbsp;</li>



<li><strong>maintainability</strong>&#8211; a modular, and organized code structure makes it easier to maintain and extend our programs.&nbsp;</li>
</ul>



<h2 class="wp-block-heading" id="h-4-is-kotlin-a-good-choice-for-dsls">4. Is Kotlin a Good Choice For DSLs?</h2>



<p>Short answer- <strong>yes</strong>.&nbsp;</p>



<p>Kotlin is designed to be a concise, readable, and expressive programming language.&nbsp;When we combine together properly named functions and function literals with receiver, we can implement truly type-safe Kotlin builders.  </p>



<p>Additionally, lambda expressions, scoping literals, extension, or infix functions help us to organize everything in an even cleaner way. </p>



<p>If you are looking for real-life usage examples, then we can find type-safe builders for example, when configuring routes in <a href="https://blog.codersee.com/category/ktor/">Ktor</a>, or in Gradle&#8217;s Kotlin DSL. </p>



<h2 class="wp-block-heading" id="h-5-function-literals-with-receiver">5. Function Literals with Receiver</h2>



<p>With all of that being said, let&#8217;s start the practice part by explaining the concept of <strong>function literals with receiver</strong>. </p>



<p>In simple words, it is nothing else than a combination of <strong>lambda expression</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 hello: (String) -> String = { name -> "Hello, $name" }

fun main() {
  val greeting = hello("Pjoter")
  
  println(greeting)  // Hello, Pjoter
}</pre>



<p>And <strong>extension function</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 String.hello() : String = "Hello, $this"

fun main() {
  val greeting = "Pjoter".hello()
  
  println(greeting)  // Hello, Pjoter
}</pre>



<p>Which combined together form:</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 hello: String.() -> String = { "Hello, $this" }

fun main() {
  val greeting = "Pjoter".hello()
  
  println(greeting)  // Hello, Pjoter
}</pre>



<p>In our example, the String object, for which we invoke the function is a&nbsp;<strong>receiver&nbsp;</strong>and it implicitly becomes&nbsp;<strong><em>this&nbsp;</em></strong>inside the function. This way, we can access all its members, like public fields, inside our function (even without <em>this</em> keyword). </p>



<p>Great, but how does this relate to creating DSLs in Kotlin? </p>



<p>Well, we can pass function literals with receiver as arguments to <strong>high-order functions</strong> (functions that take other functions as parameters). </p>



<p>I know, a mumbo jumbo, but everything will become clear by the end of this tutorial 😉</p>



<h2 class="wp-block-heading" id="h-6-implement-custom-kotlin-dsl">6. Implement Custom Kotlin DSL </h2>



<h3 class="wp-block-heading" id="h-6-1-add-high-order-function">6.1 Add High-Order Function</h3>



<p>As the first step, let&#8217;s take a look at the example 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="">enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
}

fun main() {
  val board = Board()
  board.title = "Important Tasks"
  board.color = BoardColor.GREEN
}</pre>



<p>Nothing spectacular, right? Just a simple class with mutable properties.</p>



<p>So as the next step, let&#8217;s add a higher-order function, which expects the function literal with a receiver as an argument: </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="">enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
}

fun board(init: Board.() -> Unit): Board {
  val board = Board()
  board.init()
  return board
}

fun main() {
  val board = board {
    title = "Important Tasks"
    color = BoardColor.GREEN
  }
}</pre>



<p>The <em>board</em> function is pretty straightforward- firstly, we create a new <em>Board </em>instance, and then we invoke the <em>init</em> function on it. After that, we return the updated object instance. </p>



<p>When we look at the <em>main </em>function, we can see that the only thing we do is the <em>board</em> function invocation.</p>



<p>But there is no parenthesis- &#8220;()&#8221;- right? </p>



<p>Well, technically we could use them: <code>board ( {/*some code*/ } )</code>. But in Kotlin, when the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses (<a href="https://kotlinlang.org/docs/lambdas.html#passing-trailing-lambdas" target="_blank" rel="noreferrer noopener">docs</a>). In our case- the function has only one parameter, so we don&#8217;t need parenthesis, at all.</p>



<h3 class="wp-block-heading" id="h-6-2-kotlin-type-safe-builder">6.2 Kotlin Type-Safe Builder</h3>



<p>And although at this point our logic looks like overkill, life isn’t always that easy and we may need to add more hierarchy to our code. </p>



<p>Let&#8217;s consider the following code snippet: </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="">enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

class Task {
  var title: String = ""
  var description: String = ""
}

class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
  var tasks: MutableList&lt;Task> = mutableListOf()
}

fun main() {
  val taskOne = Task() 
  taskOne.title = "Task 1"
  taskOne.description = "Task 1 description"  
  
  val taskTwo = Task() 
  taskTwo.title = "Task 2"
  taskTwo.description = "Task 2 description"   
  
  val tasks = mutableListOf(taskOne, taskTwo)  
    
  val board = Board() 
  board.title = "Important Tasks"
  board.color = BoardColor.GREEN
  board.tasks = tasks	  
}</pre>



<p>In this case, every <em>Board </em>object can contain multiple <em>Tasks </em>instances. </p>



<p>When we look at the <em>main</em> function, it&#8217;s pretty hard to see the hierarchy of objects at first glance.  </p>



<p>And now, let&#8217;s analyze the Kotlin type-safe builder approach: </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="">enum class BoardColor {
  BLACK, WHITE, GREEN, BLUE
}

class Task {
  var title: String = ""
  var description: String = ""
}

class Board {
  var title: String = ""
  var color: BoardColor = BoardColor.BLUE
  var tasks: MutableList&lt;Task> = mutableListOf()
  
  fun task(init: Task.() -> Unit) {
    val task = Task()
    task.init()
    tasks.add(task)
  }
}

fun board(init: Board.() -> Unit): Board {
  val board = Board()
  board.init()
  return board
}

fun main() {
   val board = board {
     title = "Important Tasks"
     color = BoardColor.GREEN
    
     task {
       title = "Task 1"
       description = "Task 1 description"  
     }
    
     task {
       title = "Task 2"
       description = "Task 2 description" 
     }
  }  
}</pre>



<p>We brought back the <em>board</em> function and implemented a new <em>task</em> method inside the <em>Board</em>. </p>



<p>When we implement our lambda inside the <em>main</em> function, we can access the <em>task</em> function, just like we access the <em>title</em>, or <em>color</em> property of the <em>Board </em>object. </p>



<p>To better visualize, let&#8217;s use the explicit <em>this</em> and parenthesis: </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 board = board {
  this.title = "Important Tasks"
  this.color = BoardColor.GREEN
    
  this.task( {
    this.title = "Task 1"
    this.description = "Task 1 description"  
  } )
    
  this.task( {
    this.title = "Task 2"
    this.description = "Task 2 description" 
  } )
}  </pre>



<p>To rephrase- the object, for which we invoke the function is a&nbsp;receiver&nbsp;and it implicitly becomes&nbsp;<strong><em>this&nbsp;</em>inside the function</strong>. </p>



<p>So, our <em>board</em> function first creates a new object and then invokes passed lambda on it- which sets the <em>title</em>, <em>color </em>and invokes the <em>task </em>method twice.</p>



<p>On the other hand, the <em>task</em> function also creates a new, &#8220;plain&#8221; object and also invokes passed lambda on it- this time, setting the <em>title </em>and <em>description</em> values for it. </p>



<p>And basically, we&#8217;ve just created our first, simple Kotlin type-safe builder 🙂 </p>



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



<p>In this article, we&#8217;ve learned not only how to create our custom Kotlin DSL, but also the key features which make this possible. </p>



<p>Of course, this is just an introduction, and if you are interested in learning more, for example about scope control, then let me know in the comments section 🙂</p>
<p>The post <a href="https://blog.codersee.com/kotlin-type-safe-builders-make-your-custom-dsl/">Kotlin Type-Safe Builders Explained. Implement Your Own DSL.</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/kotlin-type-safe-builders-make-your-custom-dsl/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Spring Boot 3 With Kotlin DSL. REST API Without Annotations</title>
		<link>https://blog.codersee.com/spring-boot-3-kotlin-dsl-rest-api-without-annotations/</link>
					<comments>https://blog.codersee.com/spring-boot-3-kotlin-dsl-rest-api-without-annotations/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 28 Mar 2023 06:00:42 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Kotlin]]></category>
		<category><![CDATA[Kotlin DSL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=6004437</guid>

					<description><![CDATA[<p>In this step-by-step guide, we will learn how to expose a REST API using Spring Boot 3, Kotlin, bean definition, and router DSL.</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-3-kotlin-dsl-rest-api-without-annotations/">Spring Boot 3 With Kotlin DSL. REST API Without Annotations</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>Hello ! 🙂 Continuing the DSL topic, I would like to show you how to expose a <strong>REST API</strong> using <strong>Spring Boot 3</strong>, <strong>Kotlin</strong>, <strong>bean definition</strong>, and <strong>router DSL</strong>.</p>



<p>In my <a href="https://blog.codersee.com/spring-boot-3-kotlin-router-dsl/" target="_blank" rel="noopener">previous article</a>, I showed you how the router DSL works and how to expose endpoints manually without needing any web-related annotations, like <em>@RequestMapping</em>,<em> @RestController</em>,<em> @RequestBody</em>, etc&#8230;</p>



<p>But is it possible to eliminate the annotations when defining beans? Yes, and in this article, I will prove to you that it&#8217;s possible to expose a REST API without any annotations, like @Component or any specialization.</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%;">
<p></p></div>


<a href="https://blog.codersee.com/spring-boot-3-kotlin-dsl-rest-api-without-annotations/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FqITPCIq1xxw%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p>



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



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



<p>Firstly, let&#8217;s navigate to <a href="https://start.spring.io/" target="_blank" rel="noopener">https://start.spring.io/</a> and generate a new project:</p>



<figure class="wp-block-image aligncenter"><img fetchpriority="high" decoding="async" width="1024" height="1024" src="http://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin-1024x1024.png" alt="The image is a screenshot from the start.spring.io page, which shows the setting necessary in order to create a Spring Boot 3 project with Kotlin DSL." class="wp-image-6004441" srcset="https://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin-1024x1024.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin-300x300.png 300w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin-150x150.png 150w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin-768x768.png 768w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_initializr_screenshot_spring_boot_3_kotlin.png 1261w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>As we can see, in order to expose a REST API with Spring Boot 3 and Kotlin DSL, we don&#8217;t have to add any additional dependencies. The only one is the Spring Web, which lets us build web applications.</p>



<p>Following, please import the project to your favorite IDE (like IntelliJ).</p>



<h2 class="wp-block-heading" id="h-3-add-models">3. Add Models</h2>



<p>Nextly, let&#8217;s create the <code class="EnlighterJSRAW" data-enlighter-language="raw">model</code> package and add 3 data classes to our application:</p>



<ul class="wp-block-list">
<li><code class="EnlighterJSRAW" data-enlighter-language="raw">User</code>, which will transfer the data between the app and the data source,</li>



<li><code class="EnlighterJSRAW" data-enlighter-language="raw">UserDTO</code>, which will be serialized to JSON responses,</li>



<li>and <code class="EnlighterJSRAW" data-enlighter-language="raw">ErrorResponse</code>, shipping information about backend errors.</li>
</ul>



<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="">// User.kt
data class User(
  val id: Long? = null,
  val email: String,
  val name: String,
  val age: Int
)

// UserDTO.kt
data class UserDTO(
  val email: String,
  val name: String,
  val age: Int
)

// ErrorResponse.kt
data class ErrorResponse(
  val message: String
)
</pre>



<h2 class="wp-block-heading" id="h-4-implement-a-repository">4. Implement a Repository</h2>



<p>As the next step, let&#8217;s create a <code class="EnlighterJSRAW" data-enlighter-language="raw">repository</code> package.</p>


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



<h3 class="wp-block-heading" id="h-4-1-add-userrepository-interface">4.1 Add UserRepository Interface</h3>



<p>Following, let&#8217;s introduce a <code class="EnlighterJSRAW" data-enlighter-language="raw">UserRepository</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="">interface UserRepository {
  fun create(user: User): User
  fun findAll(): List&lt;User>
  fun findById(id: Long): User?
  fun updateById(id: Long, user: User): User?
  fun deleteById(id: Long)
}
</pre>



<p>And although oftentimes I skip this part for the simplicity of my tutorials, you will later see that working with interfaces, rather than implementations gives us much more flexibility. And in real-life scenarios, we should consider using them.</p>



<h3 class="wp-block-heading" id="h-4-2-add-implementation">4.2 Add Implementation</h3>



<p>So nextly, let&#8217;s add the <code class="EnlighterJSRAW" data-enlighter-language="raw">UserCrudRepository</code> to the 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="">class UserCrudRepository(
  private val dataSource: MutableMap&lt;Long, User>
) : UserRepository {

  override fun create(user: User): User {
    val lastId = this.dataSource.keys.max()
    val incrementedId = lastId + 1
    val updatedUser = user.copy(id = incrementedId)

    this.dataSource[incrementedId] = updatedUser

    return updatedUser
  }

  override fun findAll(): List&lt;User> =
    this.dataSource.values
      .toList()

  override fun findById(id: Long): User? =
    this.dataSource[id]

  override fun updateById(id: Long, user: User): User? =
    this.dataSource[id]
      ?.let { foundUser -> 
        val updatedUser = user.copy(id = foundUser.id)
        this.dataSource[id] = updatedUser
        updatedUser
      }

  override fun deleteById(id: Long) {
    this.dataSource.remove(id)
  }
}
</pre>



<p>As we can see, this class is just a dummy in-memory database, which will be operating on the <code class="EnlighterJSRAW" data-enlighter-language="raw">dataSource</code> map provided as a constructor argument.</p>



<h3 class="wp-block-heading" id="h-4-3-implement-datasource">4.3 Implement DataSource</h3>



<p>With that being done, let&#8217;s create a new object called <code class="EnlighterJSRAW" data-enlighter-language="raw">DataSource</code> (can go to the <code class="EnlighterJSRAW" data-enlighter-language="raw">config</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="">object DataSource {

  val devDataSource: MutableMap&lt;Long, User> = mutableMapOf(
    1L to User(1L, "email-1@gmail.com", "Name 1", 22),
    2L to User(2L, "email-2@gmail.com", "Name 2", 43),
    3L to User(3L, "email-3@gmail.com", "Name 3", 26),
    4L to User(4L, "email-4@gmail.com", "Name 4", 50)
  )

  val prodDataSource: MutableMap&lt;Long, User> = mutableMapOf(
    1L to User(1L, "prod-email-1@gmail.com", "Name 1", 22),
    2L to User(2L, "prod-email-2@gmail.com", "Name 2", 43),
    3L to User(3L, "prod-email-3@gmail.com", "Name 3", 26),
    4L to User(4L, "prod-email-4@gmail.com", "Name 4", 50)
  )

}
</pre>



<p>Well, in our application we will be dealing with dummy data.</p>



<p>However, in real life instead of two maps implementations, we would rather have a few implementations of UserRepository, for example:</p>



<ul class="wp-block-list">
<li>one, in-memory, which could be used for local development and testing,</li>



<li>the second, &#8220;real&#8221;, one, responsible for database connection.</li>
</ul>



<p>Either way, the purpose of this tutorial is to learn a bit more about Kotlin with bean definition DSL, and this setup will be useful in our considerations.</p>



<h2 class="wp-block-heading" id="h-5-configure-routing">5. Configure Routing</h2>



<p>In order to expose REST endpoints with Kotlin route DSL, we need to add two things:</p>



<ul class="wp-block-list">
<li><code class="EnlighterJSRAW" data-enlighter-language="raw">UserHandler</code>&#8211; which normally would be a UserController when working with annotations,</li>



<li>custom <code class="EnlighterJSRAW" data-enlighter-language="raw">RouteFunctionDSL</code>&#8211; responsible for URL mappings.</li>
</ul>



<h3 class="wp-block-heading" id="h-5-1-add-userhandler">5.1 Add UserHandler</h3>



<p>Firstly, let&#8217;s add a <code class="EnlighterJSRAW" data-enlighter-language="raw">handler</code> package and implement the <code class="EnlighterJSRAW" data-enlighter-language="raw">UserHandler</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="">class UserHandler(
  private val userRepository: UserRepository
) {

  fun createUser(
    request: ServerRequest
  ): ServerResponse {
    val userRequest = request.body(UserDTO::class.java)

    val createdUserResponse = userRepository.create(
      user = userRequest.toModel()
    )
      .toDTO()

    return ServerResponse.ok()
      .body(createdUserResponse)
  }

  fun findAllUsers(
    request: ServerRequest
  ): ServerResponse {
    val usersResponses = userRepository.findAll()
      .map(User::toDTO)

    return ServerResponse.ok()
      .body(usersResponses)
  }

  fun findUserById(
    request: ServerRequest
  ): ServerResponse {
    val id = request.pathVariable("id")
      .toLongOrNull()
      ?: return badRequestResponse("Invalid id")

    val userResponse = userRepository.findById(id)
      ?.toDTO()

    return userResponse
      ?.let { response ->
        ServerResponse.ok()
          .body(response)
      }
      ?: notFoundResponse(id)
  }

  fun updateUserById(
    request: ServerRequest
  ): ServerResponse {
    val id = request.pathVariable("id")
      .toLongOrNull()
      ?: return badRequestResponse("Invalid id")

    val userRequest = request.body(UserDTO::class.java)

    val updatedUser = userRepository.updateById(
      id = id,
      user = userRequest.toModel()
    )

    return updatedUser
      ?.let { response ->
        ServerResponse.ok()
          .body(response)
      }
      ?: notFoundResponse(id)
  }

  fun deleteUserById(
    request: ServerRequest
  ): ServerResponse {
    val id = request.pathVariable("id")
      .toLongOrNull()
      ?: return badRequestResponse("Invalid id")

    userRepository.deleteById(id)

    return ServerResponse.noContent()
      .build()
  }

  private fun badRequestResponse(reason: String): ServerResponse =
    ServerResponse.badRequest()
      .body(
        ErrorResponse(reason)
      )

  private fun notFoundResponse(id: Long): ServerResponse =
    ServerResponse.badRequest()
      .body(
        ErrorResponse("User with id: $id was not found.")
      )

}

private fun UserDTO.toModel(): User =
  User(
    email = this.email,
    name = this.name,
    age = this.age
  )

private fun User.toDTO(): UserDTO =
  UserDTO(
    email = this.email,
    name = this.name,
    age = this.age
  )
</pre>



<p>As we can see, the handler has only one parameter of the <code class="EnlighterJSRAW" data-enlighter-language="raw">UserRepository</code>&nbsp; type. Thanks do that, <strong>we are not coupled with any implementation</strong>. Moreover, we gained flexibility and we can provide the implementation even on the fly when configuring beans.</p>



<p>Another interesting thing is the usage of <em>ServerRequests</em> and <em>ServerResponses</em>. When working with Spring Boot and Kotlin routes DSL, that&#8217;s how we deal with incoming requests and responses. And although for more details I redirect you to my previous article about this topic, I wanted to mention this because of one thing. As you can see- instead of throwing exceptions, which then Spring would translate to HTTP responses, we have the possibility to return our custom payload (and status codes, as well).</p>



<h3 class="wp-block-heading" id="h-5-2-implement-routing">5.2 Implement Routing</h3>



<p>Nevertheless, handler implementation is not sufficient. We have to instruct Spring about how HTTP requests should be handled.</p>



<p>To do so, let&#8217;s add the <code class="EnlighterJSRAW" data-enlighter-language="raw">Routes.kt</code> file inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">config</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="">fun appRouter(userHandler: UserHandler) = router {
  "/api".nest {
    "/users".nest {
      POST(userHandler::createUser)
      GET(userHandler::findAllUsers)
      GET("/{id}", userHandler::findUserById)
      PUT("/{id}", userHandler::updateUserById)
      DELETE("/{id}", userHandler::deleteUserById)
    }
  }
}
</pre>



<p>The <code class="EnlighterJSRAW" data-enlighter-language="raw">appRouter</code> function has one parameter of the <code class="EnlighterJSRAW" data-enlighter-language="raw">UserHandler</code> type, which we then use to provide references to functions inside it.</p>



<p>It&#8217;s worth mentioning that function references in Kotlin are an interesting syntactic sugar. However, if you prefer not to use them, you can always simply make use of brackets:</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="">POST { request -> userHandler.createUser(request) }
</pre>



<h2 class="wp-block-heading" id="h-6-kotlin-bean-definition-dsl">6. Kotlin Bean Definition DSL</h2>



<p>With all of that being done, we can finally learn <strong>how the Kotlin bean definition DSL work with Spring Boot 3</strong>.</p>



<h2 class="wp-block-heading" id="h-6-1-implement-beansconfig">6.1 Implement BeansConfig</h2>



<p>Firstly, let&#8217;s add a new file called <code class="EnlighterJSRAW" data-enlighter-language="raw">BeansConfig.kt</code> inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">config</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="">val beans = beans {
  // beans definitions
}
</pre>



<p>The <code class="EnlighterJSRAW" data-enlighter-language="raw">beans {}</code> is nothing else than a function, which leverages the concept of <a href="https://kotlinlang.org/docs/type-safe-builders.html" target="_blank" rel="noopener">type-safe builders</a>.</p>



<p>Secondly, let&#8217;s add the <em>BeansConfig</em> 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 BeansConfig : ApplicationContextInitializer&lt;GenericApplicationContext> {

  override fun initialize(context: GenericApplicationContext) =
    beans.initialize(context)

}
</pre>



<p>In order to register the beans in our application context, we have to invoke the <code class="EnlighterJSRAW" data-enlighter-language="raw">initialize()</code>.</p>



<p>Lastly, we need to make Spring aware of our initializer in <code class="EnlighterJSRAW" data-enlighter-language="raw">application.yaml</code>:</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="">context:
  initializer:
    classes: com.codersee.kotlindsl.config.BeansConfig
</pre>



<h2 class="wp-block-heading" id="h-6-2-autowiring-by-type">6.2 Autowiring By Type</h2>



<p>As the next step, let&#8217;s see how we can define <em>UserHandler</em> and <em>router</em> beans and instruct Spring to<strong> autowire by type</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="">bean&lt;UserHandler>()
bean(::appRouter)
</pre>



<p>As we can see, we don&#8217;t even have to specify the types of particular parameters and Spring will take care of that automatically.</p>



<p>Moreover, with<strong> callable reference</strong>, we can define a bean with the <code class="EnlighterJSRAW" data-enlighter-language="raw">appRouter</code> top-level function.</p>



<h2 class="wp-block-heading" id="h-6-3-explicitly-specify-bean-type">6.3 Explicitly Specify Bean Type</h2>



<p>Of course, if we would like to specify the type of the bean manually or wire by name, then we can do it, 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="">bean("myHandlerBean") {
  UserHandler(ref())
}
bean {
  appRouter(
    ref("myHandlerBean")
  )
}
</pre>



<p>And this time we simply make use of the ref function, which is responsible for getting a reference to beans by type (line 2), or by type and name (line 6).</p>



<h2 class="wp-block-heading" id="h-6-4-conditional-beans-based-on-profile">6.4 Conditional Beans Based On Profile</h2>



<p>Following, let&#8217;s see how we can differentiate the data source based on the profile property:</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="">profile("dev") {
  bean {
    UserCrudRepository(devDataSource)
  }
}

profile("prod") {
  bean {
    UserCrudRepository(prodDataSource)
  }
}
</pre>



<p>With this approach, beans defined inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">profile()</code> won&#8217;t be created unless the specified profile is active.</p>



<p>Of course, we can activate profiles, for example inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">application.yaml</code>:</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="">spring:
  profiles:
    active: dev
</pre>



<h2 class="wp-block-heading" id="h-6-5-define-additional-beans-properties">6.5 Define Additional Beans Properties</h2>



<p>Following, let&#8217;s see how we can customize bean definitions:</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="">bean(
  name = "userHandler",
  scope = BeanDefinitionDsl.Scope.SINGLETON,
  isLazyInit = true,
  isPrimary = true,
  isAutowireCandidate = true,
  initMethodName = "",
  destroyMethodName = "",
  description = "description",
  role = BeanDefinitionDsl.Role.APPLICATION
)
</pre>



<p>As we can see, all parameters of the <code class="EnlighterJSRAW" data-enlighter-language="raw">bean</code> function have default values assigned.</p>



<p>Nevertheless, if we would like to set any values explicitly, then we can do it with ease.</p>



<h2 class="wp-block-heading" id="h-6-6-reading-environment-variables">6.6 Reading Environment Variables</h2>



<p>As the last thing, let&#8217;s see how we can read environment variables:</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 someVariable = env.systemEnvironment["SOME_VARIABLE"]
</pre>



<p>The systemEnvironment is nothing else than a <code class="EnlighterJSRAW" data-enlighter-language="raw">Map&lt;String, Object&gt;</code> instance, which is a result of <code class="EnlighterJSRAW" data-enlighter-language="raw">System.getenv()</code> invocation.</p>



<h2 class="wp-block-heading" id="h-7-spring-boot-3-kotlin-dsl-summary">7. Spring Boot 3 Kotlin DSL Summary</h2>



<p>And that&#8217;s all for this tutorial about how to implement a REST API without annotations using Spring Boot 3, Kotlin bean definition, and router DSL.</p>



<p>I&#8217;m happy to<strong> hear your feedback</strong>&#8211; trust me, such a simple comment can motivate a lot and help me to work on my weaknesses 🙂</p>



<p>Of course, if you would like to see the source code for this article, then you can find it in this <a href="https://github.com/codersee-blog/kotlin-spring-boot-3-kotlin-bean-definition-router-dsl" target="_blank" rel="noopener">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-3-kotlin-dsl-rest-api-without-annotations/">Spring Boot 3 With Kotlin DSL. REST API Without Annotations</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/spring-boot-3-kotlin-dsl-rest-api-without-annotations/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:52:00 by W3 Total Cache
-->