<?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>Spring WebFlux Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/webflux/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:29 +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>Spring WebFlux Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Spring Boot 3 Kotlin Router DSL</title>
		<link>https://blog.codersee.com/spring-boot-3-kotlin-router-dsl/</link>
					<comments>https://blog.codersee.com/spring-boot-3-kotlin-router-dsl/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 21 Mar 2023 07:00:45 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Coroutines]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=6004428</guid>

					<description><![CDATA[<p>In this practical tutorial, I will show you how to expose web endpoints using Spring Boot 3 and Kotlin router DSL (functional style).</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-3-kotlin-router-dsl/">Spring Boot 3 Kotlin Router 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>Hello friend! 🙂 Welcome to my next practical tutorial, in which I will show you how to expose web endpoints using <strong>Spring Boot 3</strong> and <strong>Kotlin router DSL</strong> (aka- functional style).</p>



<p>Basically, the Kotlin router DSL comes in 3 variants:</p>



<ul class="wp-block-list">
<li><em>WebMvc.fn DSL</em> using <code class="EnlighterJSRAW" data-enlighter-language="raw">router { }</code> &#8211; which we can use when working with a &#8220;standard&#8221; MVC Spring Project,</li>



<li><em>WebFlux.fn Reactive DSL</em> with <code class="EnlighterJSRAW" data-enlighter-language="raw">router { }</code> &#8211; used when working with a reactive-stack framework- Spring Webflux,</li>



<li><em>WebFlux.fn Coroutines DSL</em> with <code class="EnlighterJSRAW" data-enlighter-language="raw">coRouter { }</code> &#8211; as the name suggests- the one we will use when working with coroutines.</li>
</ul>



<p>And although in this article, we will make use of the coroutine-based REST API implemented in &#8220;<a href="https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/">Reactive REST API With Spring, Kotlin, and Coroutines</a>&#8220;, the knowledge you&#8217;re gonna gain today is universal and will work regardless of the implementation.</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-router-dsl/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FfS23KwIBqfc%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-configuration">2. Project Configuration</h2>



<p>As I mentioned in the introduction, we will reuse the already implemented API and rewrite the @RestController approach using the Spring Boot Kotlin DSL router.</p>


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



<p>So, as the first step, let&#8217;s clone this <a href="https://github.com/codersee-blog/spring-boot-3-kotlin-coroutines" target="_blank" rel="noopener">GitHub repository.</a></p>



<p>When we navigate to the <code class="EnlighterJSRAW" data-enlighter-language="raw">controller</code> package, we will see that the project contains 3 controllers:</p>



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



<li>SearchController</li>



<li>CompanyController</li>
</ul>



<p>And for the purpose of this tutorial, we will focus on the API responsible for users management:</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="">@RestController
@RequestMapping("/api/users")
class UserController(
    private val userService: UserService
) {

  @PostMapping
  suspend fun createUser(@RequestBody userRequest: UserRequest): UserResponse =
    userService.saveUser(
        user = userRequest.toModel()
    )
      ?.toResponse()
      ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error during user creation.")

  @GetMapping
  suspend fun findUsers(
    @RequestParam("name", required = false) name: String?
  ): Flow {
    val users = name?.let { userService.findAllUsersByNameLike(name) }
      ?: userService.findAllUsers()

    return users.map(User::toResponse)
  }

  @GetMapping("/{id}")
  suspend fun findUserById(
    @PathVariable id: Long
  ): UserResponse =
    userService.findUserById(id)
      ?.let(User::toResponse)
      ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")

  @DeleteMapping("/{id}")
  suspend fun deleteUserById(
    @PathVariable id: Long
  ) {
    userService.deleteUserById(id)
  }

  @PutMapping("/{id}")
  suspend fun updateUser(
    @PathVariable id: Long,
    @RequestBody userRequest: UserRequest
  ): UserResponse =
    userService.updateUser(
      id = id,
      requestedUser = userRequest.toModel()
    )
      .toResponse()
}

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

fun User.toResponse(): UserResponse =
  UserResponse(
    id = this.id!!,
    email = this.email,
    name = this.name,
    age = this.age
  )
</pre>



<h2 class="wp-block-heading" id="h-3-convert-usercontroller-to-handler">3. Convert UserController To Handler</h2>



<p>Before we will start working with Spring Boot Kotlin DSL, let&#8217;s make a few adjustments.</p>



<p>Firstly, let&#8217;s add a new package called <code class="EnlighterJSRAW" data-enlighter-language="raw">handler</code>.</p>



<p>Then, let&#8217;s remove all annotations inside the class and mark the <em>UserHandler</em> with <em>@Component</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="">@Component
class UserHandler(
    private val userService: UserService
) {

  suspend fun createUser(userRequest: UserRequest): UserResponse =
    userService.saveUser(
      user = userRequest.toModel()
    )
      ?.toResponse()
      ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error during user creation.")

  suspend fun findUsers(
    name: String?
  ): Flow {
    val users = name?.let { userService.findAllUsersByNameLike(name) }
      ?: userService.findAllUsers()

    return users.map(User::toResponse)
  }

  suspend fun findUserById(
    id: Long
  ): UserResponse =
    userService.findUserById(id)
      ?.let(User::toResponse)
      ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")

  suspend fun deleteUserById(
    id: Long
  ) {
    userService.deleteUserById(id)
  }

  suspend fun updateUser(
    id: Long,
    userRequest: UserRequest
  ): UserResponse =
    userService.updateUser(
      id = id,
      requestedUser = userRequest.toModel()
    )
      .toResponse()
}
</pre>



<h2 class="wp-block-heading" id="h-4-implement-coroutines-router-with-kotlin-dsl">4. Implement Coroutines router with Kotlin DSL</h2>



<p>With all of that being done, let&#8217;s create a new class <em>Config</em> 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="">@Configuration
class Config {

  @Bean
  fun router(
    userHandler: UserHandler
  ) = coRouter {
    accept(MediaType.APPLICATION_JSON).nest {

      "/api/users".nest {
        POST("", userHandler::createUser)
        GET("", userHandler::findUsers)
        GET("/{id}", userHandler::findUserById)
        DELETE("/{id}", userHandler::deleteUserById)
        PUT("/{id}", userHandler::updateUser)
      }

    }
  }

}
</pre>



<p>As I mentioned in the beginning when working with coroutines, we will create a <strong>RouterFunction using coRouter{ }</strong>. Nevertheless, when working with MVC or Spring WebFlux, then the choice would be the <strong>router {}</strong>.</p>



<p>As we can see, the <strong>nest {}</strong> function allows us to structure routing in a neat and readable manner.</p>



<p>Let&#8217;s take a look at the following 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="">"/api/v1".nest {
  "/whatever".nest {
    "/sub-whatever".nest {
      // whatever
    }
  }
}

"/api/v2".nest {
  // another handlers
}
</pre>



<p>This way, we can manage versioning in one place.</p>



<p>Nevertheless, let&#8217;s get back to our user handlers:</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("", userHandler::createUser)
GET("", userHandler::findUsers)
GET("/{id}", userHandler::findUserById)
DELETE("/{id}", userHandler::deleteUserById)
PUT("/{id}", userHandler::updateUser)
</pre>



<p>At this point, our application won&#8217;t compile, because of one, important thing- <strong>handler functions must take only one parameter of the ServerRequest type</strong>.</p>



<p>So in order to fix the app, we will have to update our Spring controller for functional routing.</p>



<h2 class="wp-block-heading" id="h-5-update-post-endpoint">5. Update POST Endpoint</h2>



<p>So as the first step, let&#8217;s make the necessary adjustments to the <code class="EnlighterJSRAW" data-enlighter-language="raw">createUser</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="">suspend fun createUser(request: ServerRequest): ServerResponse {
  val userRequest = request.awaitBody(UserRequest::class)

  val savedUserResponse = userService.saveUser(
    user = userRequest.toModel()
  )
    ?.toResponse()
    ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error during user creation.")

  return ServerResponse.ok()
    .bodyValueAndAwait(savedUserResponse)
}

// Before:


@PostMapping
suspend fun createUser(@RequestBody userRequest: UserRequest): UserResponse =
  userService.saveUser(
    user = userRequest.toModel()
  )
    ?.toResponse()
    ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error during user creation.")
</pre>



<p>As we can see, instead of binding the JSON payload to the UserRequest instance with an annotation, we have to do it manually with <code class="EnlighterJSRAW" data-enlighter-language="raw">.awaitBody()</code>.</p>



<p>Whatsoever, the return type changes to <strong>ServerResponse</strong>, which we have to create manually.</p>



<p>And although the final choice is up to you, instead of throwing the ResponseStatusException, we can compose the HTTP error response on our own:</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="">suspend fun createUser(request: ServerRequest): ServerResponse {
  val userRequest = request.awaitBody(UserRequest::class)

  return userService.saveUser(
    user = userRequest.toModel()
  )
    ?.toResponse()
    ?.let { response ->
      ServerResponse.ok()
        .bodyValueAndAwait(response)
    }
    ?: ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
      .buildAndAwait()
}
</pre>



<h2 class="wp-block-heading" id="h-6-migrate-get-all-users">6. Migrate GET All Users</h2>



<p>Nextly, let&#8217;s migrate the <code class="EnlighterJSRAW" data-enlighter-language="raw">findUsers()</code> function and learn how to read query parameters:</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="">suspend fun findUsers(
  request: ServerRequest
): ServerResponse {
  val users = request.queryParamOrNull("name")
    ?.let { name -> userService.findAllUsersByNameLike(name) }
    ?: userService.findAllUsers()

  val usersResponse = users.map(User::toResponse)

  return ServerResponse.ok()
    .bodyAndAwait(usersResponse)
}

// Before:

@GetMapping
suspend fun findUsers(
  @RequestParam("name", required = false) name: String?
): Flow {
  val users = name?.let { userService.findAllUsersByNameLike(name) }
    ?: userService.findAllUsers()

  return users.map(User::toResponse)
} 
</pre>



<p>This time, in order to read a particular query parameter, we can use the <code class="EnlighterJSRAW" data-enlighter-language="raw">queryParamOrNull(String)</code>. However, the ServerRequest interface comes with two, additional functions, which we could use here:</p>



<ul class="wp-block-list">
<li><code class="EnlighterJSRAW" data-enlighter-language="raw">queryParam(String)</code>, which returns the <em>Optional</em>,</li>



<li>and <code class="EnlighterJSRAW" data-enlighter-language="raw">queryParams()</code> returning an instance of <em>MultiValueMap&lt;String, String&gt;</em> with all parameters.</li>
</ul>



<p>And just, like previously we compose a ServerResponse instance, but this time using the <code class="EnlighterJSRAW" data-enlighter-language="raw">bodyAndAwait()</code>, which takes a <em>Flow</em> as an argument.</p>



<h2 class="wp-block-heading" id="h-7-get-user-by-id">7. GET User By Id</h2>



<p>As the next example in our Spring Boot Kotlin DSL project, let&#8217;s rewrite the <code class="EnlighterJSRAW" data-enlighter-language="raw">findUserById()</code> and see how we can read a <strong>path variable</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="">suspend fun findUserById(
  request: ServerRequest
): ServerResponse {
  val id = request.pathVariable("id").toLong()

  val userResponse = userService.findUserById(id)
    ?.let(User::toResponse)
    ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")

  return ServerResponse.ok()
    .bodyValueAndAwait(userResponse)
}

// Before:

@GetMapping("/{id}")
suspend fun findUserById(
  @PathVariable id: Long
): UserResponse =
  userService.findUserById(id)
    ?.let(User::toResponse)
    ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")
</pre>



<p>As we can see, in order to read the path variable, we have to use the <code class="EnlighterJSRAW" data-enlighter-language="raw">pathVariable(String)</code> (or alternatively, <code class="EnlighterJSRAW" data-enlighter-language="raw">pathVariables()</code>). Unfortunately, this function cannot be parametrized and we have to take care of Long conversions manually.</p>



<h2 class="wp-block-heading" id="h-8-delete-endpoint">8. DELETE Endpoint</h2>



<p>Following, let&#8217;s update the <code class="EnlighterJSRAW" data-enlighter-language="raw">deleteUserById()</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="">suspend fun deleteUserById(
  request: ServerRequest
): ServerResponse {
  val id = request.pathVariable("id").toLong()

  userService.deleteUserById(id)

  return ServerResponse.noContent()
    .buildAndAwait()
}

// Before: 

@DeleteMapping("/{id}")
suspend fun deleteUserById(
  @PathVariable id: Long
) {
  userService.deleteUserById(id)
}
</pre>



<p>And this time, instead of returning anything in the response payload, we make use of the <code class="EnlighterJSRAW" data-enlighter-language="raw">noContent()</code> along with <code class="EnlighterJSRAW" data-enlighter-language="raw">buildAndAwait()</code> to return a bodiless entity.</p>



<h2 class="wp-block-heading" id="h-9-user-update-with-put">9. User Update With PUT</h2>



<p>Lastly, let&#8217;s make the necessary adjustments to the <code class="EnlighterJSRAW" data-enlighter-language="raw">updateUser()</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="">suspend fun updateUser(
  request: ServerRequest
): ServerResponse {
  val id = request.pathVariable("id").toLong()
  val userRequest = request.awaitBody(UserRequest::class)

  val userResponse = userService.updateUser(
    id = id,
    requestedUser = userRequest.toModel()
  ).toResponse()

  return ServerResponse.ok()
    .bodyValueAndAwait(userResponse)
}

// Before:

@PutMapping("/{id}")
suspend fun updateUser(
  @PathVariable id: Long,
  @RequestBody userRequest: UserRequest
): UserResponse =
  userService.updateUser(
    id = id,
    requestedUser = userRequest.toModel()
  )
    .toResponse()
</pre>



<p>As we can see, nothing new shows up in this example.</p>



<h2 class="wp-block-heading" id="h-10-testing-and-homework">10. Testing And Homework</h2>



<p>With all of that being done, we can finally run our application and verify whether handlers are working, as expected.</p>



<p>Your homework will be to:</p>



<ol class="wp-block-list">
<li>Test whether the routing works fine after we converter our Spring controller to the functional style.</li>



<li>Refactor the <em>CompanyController</em> and <em>SearchController</em> classes in a similar manner.</li>
</ol>



<p>As a bonus, right here you can find a <a href="https://drive.google.com/file/d/1xq3njZk2oGwHQGZkTFf05VwHr1pdsJJE/view?usp=sharing" target="_blank" rel="noopener">Postman collection</a>, which you can use for testing.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Note: If you would like to see the solution, then check out the <a href="https://github.com/codersee-blog/spring-boot-3-kotlin-coroutines/tree/router-dsl" target="_blank" rel="noopener">router-dsl branch</a> in my GitHub repo.</p>
</blockquote>



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



<p>And that&#8217;s all for this tutorial, in which we have learned how to expose web endpoints using <strong>Spring Boot 3</strong> and <strong>Kotlin router DSL</strong> (aka- functional style).</p>



<p>Hope you enjoyed this content, and if so, then please <strong>reach out in the comments section</strong>.</p>



<p>Have a great week!</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-3-kotlin-router-dsl/">Spring Boot 3 Kotlin Router 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/spring-boot-3-kotlin-router-dsl/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Spring WebClient With Kotlin Coroutines</title>
		<link>https://blog.codersee.com/spring-webclient-with-kotlin-coroutines/</link>
					<comments>https://blog.codersee.com/spring-webclient-with-kotlin-coroutines/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 07 Mar 2023 07:00:42 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Coroutines]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<category><![CDATA[WebClient]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=6004406</guid>

					<description><![CDATA[<p>In this, revisited article I will show you everything you need to know when working with Spring WebClient and Kotlin coroutines.</p>
<p>The post <a href="https://blog.codersee.com/spring-webclient-with-kotlin-coroutines/">Spring WebClient With Kotlin Coroutines</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>Welcome to my next article, in which I will show you everything you need to know when working with <strong>Spring WebClient and Kotlin coroutines</strong>.</p>



<p>To be on the same page- this article will be a coroutine-focused extension of my other blog post about <a href="https://blog.codersee.com/spring-5-webclient-with-spring-boot/" target="_blank" rel="noopener">Spring 5 WebClient with Spring Boot</a>. So, instead of duplicating the content, which works exactly the same regardless of whether we pick the Reactor or coroutines implementation, I will focus on response handling. Nevertheless, if you are interested in other features, like request bodies, headers, and filter implementation, then I recommend checking out that article, as well.</p>



<p>With that being said, 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%;">
<p></p></div>


<a href="https://blog.codersee.com/spring-webclient-with-kotlin-coroutines/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FS8ie6zyJHsw%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-api-specification">2. API Specification</h2>



<p>Before I show you how to implement WebClient with Kotlin coroutines, let me quickly describe the API we are about to query.</p>



<p>Basically, we will be using 3 endpoints in order to:</p>



<ul class="wp-block-list">
<li>fetch a list of all users,</li>



<li>get user by its identifier,</li>



<li>and delete a particular user by id.</li>
</ul>



<p>And to better visualize, let&#8217;s take a look at the API &#8220;contract&#8221;:</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:8090/users 

# Example response:
[
  {
    "id": 1,
    "first_name": "Robert",
    "last_name": "Smith"
  },
  {
    "id": 2,
    "first_name": "Mary",
    "last_name": "Jones"
  }
...
]

GET http://localhost:8090/users/1

# Example successful response body:
{
  "id": 1,
  "first_name": "Robert",
  "last_name": "Smith"
}

# If the user is not found, then the API returns 404 NOT FOUND

DELETE http://localhost:8090/users/1

# Responds 204 No Content with empty body</pre>


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



<h2 class="wp-block-heading" id="h-3-configure-spring-webclient">3. Configure Spring WebClient</h2>



<p>And although the Spring WebClient configuration does not differ when working with coroutines, let&#8217;s take a look at the config 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="">@Configuration
class Config {

  @Bean
  fun webClient(builder: WebClient.Builder): WebClient =
      builder
        .baseUrl("http://localhost:8090")
        .build()
}
</pre>



<p>As we can see, with this config we define a new WebClient bean and instruct that a base URL for our requests is <code class="EnlighterJSRAW" data-enlighter-language="raw">http://localhost:8090</code>.</p>



<h2 class="wp-block-heading" id="h-4-implement-userresponse">4. Implement UserResponse</h2>



<p>Nextly, let&#8217;s add the UserResponse class 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="">data class UserResponse(
  val id: Long,
  @JsonProperty("first_name") val firstName: String,
  @JsonProperty("last_name") val lastName: String
)
</pre>



<p>And this class will be responsible for serializing responses from the external API.</p>



<h2 class="wp-block-heading" id="h-5-spring-webclient-with-kotlin-coroutines">5. Spring WebClient With Kotlin Coroutines</h2>



<p>As the next step, we can finally implement the UserApiService and start querying.</p>



<p>Of course, let&#8217;s inject the WebClient before heading to the next steps:</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="">@Service
class UserApiService(
  private val webClient: WebClient
) {

}
</pre>



<h3 class="wp-block-heading" id="h-5-1-obtaining-response-body-with-awaitbody">5.1 Obtaining Response Body With awaitBody()</h3>



<p>Firstly, let&#8217;s learn how to fetch a list of users:</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="">suspend fun findAllUsers(): List&lt;UserResponse> =
  webClient.get()
    .uri("/users")
    .retrieve()
    .awaitBody&lt;List&lt;UserResponse>>()</pre>



<p>Well, we have to remember that the <code class="EnlighterJSRAW" data-enlighter-language="generic">awaitBody()</code> is a suspend function- and that&#8217;s the reason why our function is defined as a suspend, as well. Moreover, we have to define the type of response we are expecting (<code class="EnlighterJSRAW" data-enlighter-language="generic">List&lt;UserResponse&gt;</code> in our case). Of course, it&#8217;s optional if we already defined a return type explicitly for our function.</p>



<h3 class="wp-block-heading" id="h-5-2-awaitbodyornull">5.2 awaitBodyOrNull()</h3>



<p>Additionally, we can make use of another variant, <code class="EnlighterJSRAW" data-enlighter-language="raw">awaitBodyOrNull()</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="">suspend fun findUserById(id: Long): UserResponse? =
  webClient.get()
    .uri("/users/$id")
    .retrieve()
    .awaitBodyOrNull&lt;UserResponse>()</pre>



<p>This time, if the Mono completes without a value, a null is returned.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Nevertheless, please keep in mind that by default when using retrieve() 4xx and 5xx responses result in a WebClientResponseException. Later, I will show you how to customize this behavior using onStatus().</p>
</blockquote>



<h3 class="wp-block-heading" id="h-5-3-return-user-list-as-a-flow">5.3 Return User List As A Flow</h3>



<p>As the next step, let&#8217;s learn how to transform our publisher into Flow:</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 findAllUsersFlow(): Flow&lt;UserResponse> =
  webClient.get()
    .uri("/users")
    .retrieve()
    .bodyToFlow&lt;UserResponse>()</pre>



<p>This time, instead of converting users&#8217; responses into a List, we invoke the <code class="EnlighterJSRAW" data-enlighter-language="raw">.bodyToFlow&lt;UserResponse&gt;()</code>, which underneath transforms a <code class="EnlighterJSRAW" data-enlighter-language="raw">Flux&lt;UserResponse&gt;</code> into the Flow.</p>



<h3 class="wp-block-heading" id="h-5-4-webclient-retrieve-vs-exchange">5.4 WebClient retrieve vs exchange</h3>



<p>Well, although I promised not to cover things twice, I believe it&#8217;s important to say a couple of words here.</p>



<p>The main difference between <code class="EnlighterJSRAW" data-enlighter-language="raw">retrieve()</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">exchange()</code> methods is that the <code class="EnlighterJSRAW" data-enlighter-language="raw">exchange()</code> returns additional HTTP information, like headers and status. <strong>Nevertheless</strong>, when using it, <strong>it is our responsibility to handle all response cases to avoid memory leaks!&nbsp;</strong>And because of that, the <code class="EnlighterJSRAW" data-enlighter-language="raw">exchange()</code> was deprecated in Spring 5.3 and we should rather use <code class="EnlighterJSRAW" data-enlighter-language="raw">exchangeToMono(Function)</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">exchangeToFlux(Function)</code>(of course, if the <code class="EnlighterJSRAW" data-enlighter-language="raw">retrieve()</code> is not sufficient for our needs).</p>



<p>When working with Spring WebClient and Kotlin coroutines, we can make use of the <code class="EnlighterJSRAW" data-enlighter-language="raw">awaitExchange</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">exchangeToFlow</code> functions.</p>



<h3 class="wp-block-heading" id="h-5-5-awaitexchange">5.5 awaitExchange()</h3>



<p>With that being said, let&#8217;s implement another function using <strong>awaitExchange</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="">suspend fun findAllUsersUsingExchange(): List&lt;UserResponse> =
  webClient.get()
    .uri("/users")
    .awaitExchange { clientResponse ->
      val headers = clientResponse.headers().asHttpHeaders()
      logger.info("Received response from users API. Headers: $headers")
      clientResponse.awaitBody&lt;List&lt;UserResponse>>()
    }</pre>



<p>As we can see, this function can be a good choice if we would like to access additional response information (like headers in our case) and perform additional logic based on them.</p>



<h3 class="wp-block-heading" id="h-5-6-exchangetoflow">5.6 exchangeToFlow()</h3>



<p>On the other hand, if we would like to return the Flow, then we must choose <strong>exchangeToFlow</strong> variant:</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 findAllUsersFlowUsingExchange(): Flow&lt;UserResponse> =
  webClient.get()
    .uri("/users")
    .exchangeToFlow { clientResponse ->
      val headers = clientResponse.headers().asHttpHeaders()
      logger.info("Received response from users API. Headers: $headers")
      clientResponse.bodyToFlow&lt;UserResponse>()
    }</pre>



<h3 class="wp-block-heading" id="h-5-7-work-with-bodiless">5.7 Work With Bodiless</h3>



<p>Sometimes, the REST API endpoints do not return any response body, which is usually indicated by the 204 No Content status code.</p>



<p>In such a case we can either choose awaitBody and pass the Unit type, or awaitBodilessEntity:</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="">suspend fun deleteUserById(id: Long): Unit =
  webClient.delete()
    .uri("/users/$id")
    .retrieve()
    .awaitBody&lt;Unit>()

suspend fun deleteUserById(id: Long): ResponseEntity&lt;Void> =
  webClient.delete()
    .uri("/users/$id")
    .retrieve()
    .awaitBodilessEntity()</pre>



<p>As we can see, depending on our needs we can either simply return the Unit or the ResponseEntity instance.</p>



<h3 class="wp-block-heading" id="h-5-8-handling-errors">5.8 Handling Errors</h3>



<p>And although the last example does not differ when working with WebClient and Kotlin coroutines, I feel obliged to show how we can handle API error status codes.</p>



<p>As I mentioned previously, by default all 4xx and 5xx response status codes cause WebClientResponseException to be thrown. And if we would like to alter this behavior, Spring WebFlux comes with a useful <code class="EnlighterJSRAW" data-enlighter-language="raw">onStatus</code> method for that:</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="">suspend fun findUserByIdNotFoundHandling(id: Long): UserResponse =
  webClient.get()
    .uri("/users/$id")
    .retrieve()
    .onStatus({ responseStatus ->
      responseStatus == HttpStatus.NOT_FOUND
    }) { throw ResponseStatusException(HttpStatus.NOT_FOUND) }
    .awaitBody&lt;UserResponse>()</pre>



<p>The <code class="EnlighterJSRAW" data-enlighter-language="raw">onStatus</code> takes two parameters: the predicate and a function. In our example, each 404 Not Found response from the external API will be translated to ResponseStatusException with the same HttpStatus.</p>



<h2 class="wp-block-heading" id="h-6-spring-webclient-with-kotlin-coroutines-summary">6. Spring WebClient With Kotlin Coroutines Summary</h2>



<p>And that&#8217;s all for this article. Together, we&#8217;ve learned how easily we can implement a Spring WebClient with Kotlin coroutines using built-in features.</p>



<p>As always, you can find the example source code in this <a href="https://github.com/codersee-blog/spring-webclient-kotlin-coroutines" target="_blank" rel="noopener">GitHub repository</a> and I will be thankful if you would like to share some feedback with me right here, in the comments section.</p>
<p>The post <a href="https://blog.codersee.com/spring-webclient-with-kotlin-coroutines/">Spring WebClient With Kotlin Coroutines</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-webclient-with-kotlin-coroutines/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Reactive REST API With Spring, Kotlin, and Coroutines</title>
		<link>https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/</link>
					<comments>https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 02 Mar 2023 07:00:00 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Coroutines]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=6004383</guid>

					<description><![CDATA[<p>In this practical guide, I will show you how to create a reactive REST API using Spring, Kotlin, coroutines, and Kotlin Flows.</p>
<p>The post <a href="https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/">Reactive REST API With Spring, Kotlin, and Coroutines</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 friend! In this, practical step-by-step guide, I will teach you how to create a reactive <strong>REST API using Spring, Kotlin, coroutines, and Kotlin Flows</strong> entirely from scratch.</p>



<p>And although Spring internally uses Reactor implementation, coroutines provide a more straightforward and natural way of writing asynchronous, non-blocking code. Thanks to this, we can enjoy the benefits of a non-blocking code without <span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">compromising the readability of the code (which might become an issue when using Project Reactor in more mature and complex projects).</span></span></span></p>



<p>At the end of this tutorial, you will know precisely how to:</p>



<ul class="wp-block-list">
<li>set up a <strong>Spring Boot 3</strong> project to work with <strong>Kotlin coroutines</strong>,</li>



<li>run a <strong>PostgreSQL</strong> instance using Docker,</li>



<li>create an example <strong>database and a schema</strong>,</li>



<li>query and persist data using <strong>R2DBC</strong> and <strong>CoroutineCrudRepository</strong>,</li>



<li>expose reactive REST API with <strong>coroutines</strong> and <strong>Kotlin Flows</strong>.</li>
</ul>



<p>So, 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%;">
<p></p></div>


<a href="https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FORPWK0NSPiw%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-postgresql-db">2. PostgreSQL DB</h2>



<p>As the first step, let&#8217;s learn how to set up a fresh PostgreSQL instance using Docker and populate it with the necessary data. Of course, feel free to skip step 2.1 if you already have some development database installed.</p>



<h3 class="wp-block-heading" id="h-2-1-start-a-postgres-instance">2.1 Start a Postgres Instance</h3>



<p>In order to start a fresh instance, let&#8217;s navigate to 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 some-postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres</pre>



<p>After that, let&#8217;s wait a while until the database is up and running.</p>



<p>We can additionally verify if that&#8217;s the case using <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</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="">CONTAINER ID IMAGE    COMMAND                CREATED     STATUS           PORTS                  NAMES
1863e6ec964a postgres "docker-entrypoint.s…" 6 hours ago Up About an hour 0.0.0.0:5432->5432/tcp some-postgres</pre>



<p>This one, simple command results in a couple of interesting things happening underneath:</p>



<ul class="wp-block-list">
<li>firstly, it creates a new container named <em>some-postgres</em> and exposes its port 5432 to 5432 of the host machine (localhost in most cases),</li>



<li>secondly, it creates a default Postgres user called <em>postgres</em> and sets its password using the POSTGRES_PASSWORD environment value (of course, we can create another user using the POSTGRES_USER environment variable),</li>



<li>and lastly, it starts in a detached mode thanks to the <code data-enlighter-language="raw" class="EnlighterJSRAW">-d</code> flag.</li>
</ul>


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



<h3 class="wp-block-heading" id="h-2-2-create-and-populate-the-database">2.2 Create And Populate The Database</h3>



<p>As the next step, let&#8217;s connect to the database, and create a schema with two tables:</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 schema if not exists application;

create table if not exists application.company(
  id serial not null primary key,
  name varchar(255) not null,
  address varchar(255) not null
);

create table if not exists application.app_user(
  id serial not null primary key,
  email varchar(255) not null unique,
  name varchar(255) not null,
  age int not null, 
  company_id bigint not null references application.company(id) on delete cascade
);
</pre>



<p>As we can see, the purpose of our application will be users and companies management. Each user will have to be assigned to some company. Moreover, if a company is deleted, then the related user rows will be removed, as well.</p>



<p>Additionally, we can populate tables with the following script:</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="">insert into application.company(name, address) values
  ('Company 1', 'Address 1'),
  ('Company 2', 'Address 2'),
  ('Company 3', 'Address 3');

insert into application.app_user(email, name, age, company_id) values
  ('email-1@codersee.com', 'John', 23, 1),
  ('email-2@codersee.com', 'Adam', 33, 1),
  ('email-3@codersee.com', 'Maria', 40, 2),
  ('email-4@codersee.com', 'James', 39, 3),
  ('email-5@codersee.com', 'Robert', 41, 3),
  ('email-6@codersee.com', 'Piotr', 28, 3);
</pre>



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



<p>With that being done, let&#8217;s navigate to the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr</a> page and generate a new project:</p>



<figure class="wp-block-image aligncenter"><img fetchpriority="high" decoding="async" width="1488" height="807" src="http://blog.codersee.com/wp-content/uploads/2023/03/spring_boot_3_kotlin_coroutines_spring_initializr_page.png" alt="Image is a screenshot from the Spring Initializr page showing necessary configuration to create a Spring Boot 3 Kotlin coroutines project. " class="wp-image-6004389" srcset="https://blog.codersee.com/wp-content/uploads/2023/03/spring_boot_3_kotlin_coroutines_spring_initializr_page.png 1488w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_boot_3_kotlin_coroutines_spring_initializr_page-300x163.png 300w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_boot_3_kotlin_coroutines_spring_initializr_page-1024x555.png 1024w, https://blog.codersee.com/wp-content/uploads/2023/03/spring_boot_3_kotlin_coroutines_spring_initializr_page-768x417.png 768w" sizes="(max-width: 1488px) 100vw, 1488px" /></figure>



<p>The above configuration is all we need in order to create a fresh Spring Boot 3 project with Kotlin and coroutines. Additionally, in order to connect to the Postgres database, we need two more dependencies: Spring Data R2DBC and PostgreSQL Driver.</p>



<p>With that being done, let&#8217;s hit the <em>Generate </em>button and import the project to our IDE (you can find a video on how to configure <a href="https://youtu.be/npib90rBLE4" target="_blank" rel="noopener">IntelliJ IDEA for Kotlin right here</a>).</p>



<h2 class="wp-block-heading" id="h-4-configure-r2dbc-connection">4. Configure R2DBC Connection</h2>



<p>Nextly, let&#8217;s open up the <code class="EnlighterJSRAW" data-enlighter-language="raw">application.properties</code> file, change its extension to <code class="EnlighterJSRAW" data-enlighter-language="raw">.yaml</code>, and insert the connection config:</p>



<div>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  r2dbc:
    url: r2dbc:postgresql://${DB_HOST:localhost}:5432/
    username: ${DB_USERNAME:postgres}
    password: ${DB_PASSWORD:postgres}</pre>
</div>



<p>This configuration instructs Spring to check DB_HOST, DB_USERNAME, and DB_PASSWORD environment variables first. If a particular variable is missing, then we provide the default values<em>&#8211; localhost</em> and <em>postgres.</em></p>



<h2 class="wp-block-heading" id="h-5-create-models">5. Create Models</h2>



<p>Following, let&#8217;s create a new package called <code class="EnlighterJSRAW" data-enlighter-language="raw">model</code> and introduce classes responsible for mapping database tables.</p>



<p>As the first one, let&#8217;s implement the <em>Company</em>:</p>



<div>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Table("application.company")
data class Company(
    @Id val id: Long? = null,
    val name: String,
    val address: String
)</pre>
</div>



<p>The <em>@Table</em> and <em>@Id</em> annotations are pretty descriptive and they are necessary in order to configure mapping in Spring. Nevertheless, it&#8217;s worth mentioning that if we do not want to generate identifiers manually, then the identifier fields have to be nullable.</p>



<p>Similarly, let&#8217;s create the <em>User</em> data 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="">@Table("application.app_user")
data class User(
    @Id val id: Long? = null,
    val email: String,
    val name: String,
    val age: Int,
    val companyId: Long
)
</pre>



<h2 class="wp-block-heading" id="h-6-crud-operations-using-kotlin-coroutines">6. CRUD operations using Kotlin Coroutines</h2>



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



<p>In our project, we will utilize the <code class="EnlighterJSRAW" data-enlighter-language="raw">CoroutineCrudRepository</code>&#8211; a dedicated Spring Data repository built on Kotlin coroutines. If you&#8217;ve ever been working with Reactor, then <span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">in a nutshell, <em>Mono&lt;T&gt;</em> functions are replaced with suspended functions returning the type <em>T</em>, and instead of creating <em>Fluxes</em>, we will generate <em>Flows</em>. On the other hand, if you have never worked with Reactor</span></span></span><span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">, then Flow&lt;T&gt; return type means that a function returns multiple asynchronously computed values suspend function returns only a single value.<br></span></span></span></p>



<p><span class="HwtZe" lang="en"><span class="jCAhz ChMk0b"><span class="ryNqvb">Of course, this is a simplification and if you would like to learn the differences between Fluxes and Flows, then let me know in the comments. </span></span></span></p>



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



<p>To kick things off, let&#8217;s implement the <em>UserRepository</em> interface:</p>



<div>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">interface UserRepository : CoroutineCrudRepository&lt;User, Long&gt; {
    
  fun findByNameContaining(name: String): Flow&lt;User&gt;
    
  fun findByCompanyId(companyId: Long): Flow&lt;User&gt;

  @Query("SELECT * FROM application.app_user WHERE email = :email")
  fun randomNameFindByEmail(email: String): Flow&lt;User&gt;
}

</pre>
</div>



<p>The <code class="EnlighterJSRAW" data-enlighter-language="raw">CoroutineCrudRepository</code> extends the Spring Data <code class="EnlighterJSRAW" data-enlighter-language="raw">Repository</code> and requires us to provide two types: the domain type and the identifier type- a <em>User</em> and a <em>Long</em> in our case. This interface comes with 15 already implemented functions, like <em>save</em>, <em>findAll</em>, <em>delete</em>, etc.- responsible for generic CRUD operations. This way, we can tremendously reduce the amount of boilerplate in our Kotlin codebase.</p>



<p>Moreover, we make use of two, great features of Spring Data (which are not Kotlin, or coroutines specific):</p>



<ul class="wp-block-list">
<li><a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details" target="_blank" rel="noopener">Query Methods</a>, which in simple terms allow us to define queries <strong>through function names</strong>. Just like above- the <code class="EnlighterJSRAW" data-enlighter-language="raw">findByNameContaining</code> will be translated into <code class="EnlighterJSRAW" data-enlighter-language="raw">where like..</code> query and&nbsp; <code class="EnlighterJSRAW" data-enlighter-language="raw">findByCompanyId</code> will let us search users by company identifier.</li>



<li><strong>@Query</strong>, which lets us execute both JPQL and native SQL queries.</li>
</ul>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Note: I&#8217;ve named the third method randomNameFindByEmail just to emphasize, that the function name is irrelevant when using the Query, don&#8217;t do that in your codebase 😀</p>
</blockquote>



<h3 class="wp-block-heading" id="h-6-2-add-companyrepository">6.2 Add CompanyRepository</h3>



<p>Nextly, let&#8217;s add the CompanyRepository with only one custom 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="">@Repository
interface CompanyRepository : CoroutineCrudRepository&lt;Company, Long> {
    fun findByNameContaining(name: String): Flow&lt;Company>
}</pre>



<h2 class="wp-block-heading" id="h-7-create-services">7. Create Services</h2>



<p>With model and repository layer implemented, we can move on and create a <code class="EnlighterJSRAW" data-enlighter-language="raw">service</code> package.</p>



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



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

    suspend fun saveUser(user: User): User? =
        userRepository.randomNameFindByEmail(user.email)
            .firstOrNull()
            ?.let { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "The specified email is already in use.") }
            ?: userRepository.save(user)

    suspend fun findAllUsers(): Flow&lt;User> =
        userRepository.findAll()

    suspend fun findUserById(id: Long): User? =
        userRepository.findById(id)

    suspend fun deleteUserById(id: Long) {
        val foundUser = userRepository.findById(id)

        if (foundUser == null)
            throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")
        else
            userRepository.deleteById(id)
    }

    suspend fun updateUser(id: Long, requestedUser: User): User {
        val foundUser = userRepository.findById(id)

        return if (foundUser == null)
            throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")
        else
            userRepository.save(
                requestedUser.copy(id = foundUser.id)
            )
    }

    suspend fun findAllUsersByNameLike(name: String): Flow&lt;User> =
        userRepository.findByNameContaining(name)

    suspend fun findUsersByCompanyId(id: Long): Flow&lt;User> =
        userRepository.findByCompanyId(id)
}
</pre>



<p>All the magic starts with the <em>@Service</em> annotation, which is a specialization of a <em>@Component</em>. This way, we simply instruct Spring to create a bean of UserService.</p>



<p>As we can clearly see, our service logic is really straightforward, and thanks to the coroutines we can write code similar to imperative programming.</p>



<p>Lastly, I just wanted to mention the logic responsible for User updates. The <code class="EnlighterJSRAW" data-enlighter-language="raw">save</code> method of the <code class="EnlighterJSRAW" data-enlighter-language="raw">Repository</code> interface works in two ways:</p>



<ul class="wp-block-list">
<li>when the value of a field marked with <em>@Id</em> is set to null, a new entry will be created in the database,</li>



<li>nevertheless, if the id is not null, then the row with the specified will be updated.</li>
</ul>



<h3 class="wp-block-heading" id="h-7-2-create-companyservice">7.2 Create CompanyService</h3>



<p>Following, let&#8217;s implement the <code class="EnlighterJSRAW" data-enlighter-language="raw">CompanyService</code> responsible for companies management:</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="">@Component
class CompanyService(
    private val companyRepository: CompanyRepository
) {

    suspend fun saveCompany(company: Company): Company? =
        companyRepository.save(company)

    suspend fun findAllCompanies(): Flow&lt;Company> =
        companyRepository.findAll()

    suspend fun findCompanyById(id: Long): Company? =
        companyRepository.findById(id)

    suspend fun deleteCompanyById(id: Long) {
        val foundCompany = companyRepository.findById(id)

        if (foundCompany == null)
            throw ResponseStatusException(HttpStatus.NOT_FOUND, "Company with id $id was not found.")
        else
            companyRepository.deleteById(id)
    }

    suspend fun findAllCompaniesByNameLike(name: String): Flow&lt;Company> =
        companyRepository.findByNameContaining(name)

    suspend fun updateCompany(id: Long, requestedCompany: Company): Company {
        val foundCompany = companyRepository.findById(id)

        return if (foundCompany == null)
            throw ResponseStatusException(HttpStatus.NOT_FOUND, "Company with id $id was not found.")
        else
            companyRepository.save(
                requestedCompany.copy(id = foundCompany.id)
            )
    }

}
</pre>



<h2 class="wp-block-heading" id="h-8-implement-controllers">8. Implement Controllers</h2>



<p>And the last thing we need to implement in our Spring Kotlin Coroutines project are&#8230; REST endpoints (and a couple of DTOs 😉 ).</p>



<h3 class="wp-block-heading" id="h-8-1-create-userresponse-and-userrequest">8.1. Create UserResponse and UserRequest</h3>



<p>When working in real-life scenarios we can use different approaches, when it comes to serialization and deserialization of data (or in simple terms- JSON &lt;-&gt; Kotlin objects conversions). In some cases dealing with model classes might be sufficient, but introducing DTOs will usually be a better approach. In our examples, we will introduce separate request and response classes, which in my opinion let us maintain our codebase much easier.</p>



<p>To do so, let&#8217;s add two data classes to our codebase- the <code class="EnlighterJSRAW" data-enlighter-language="raw">UserRequest</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">UserResponse</code> (inside the <code class="EnlighterJSRAW" data-enlighter-language="raw">dto</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="">data class UserRequest(
    val email: String,
    val name: String,
    val age: Int,
    @JsonProperty("company_id") val companyId: Long
)

data class UserResponse(
    val id: Long,
    val email: String,
    val name: String,
    val age: Int
)</pre>



<p>Request classes will be used to translate JSON payload into Kotlin objects, whereas the response ones will do the opposite.</p>



<p>Additionally, we make use of the <code class="EnlighterJSRAW" data-enlighter-language="raw">@JsonProperty</code> annotation, so that our JSON files will use the <strong>snake case</strong>.</p>



<h3 class="wp-block-heading" id="h-8-2-implement-usercontroller">8.2. Implement UserController</h3>



<p>With that prepared, we have nothing else to do than create a <code class="EnlighterJSRAW" data-enlighter-language="raw">controller</code> package and implement the UserController:</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="">@RestController
@RequestMapping("/api/users")
class UserController(
    private val userService: UserService
) {

    @PostMapping
    suspend fun createUser(@RequestBody userRequest: UserRequest): UserResponse =
        userService.saveUser(
            user = userRequest.toModel()
        )
            ?.toResponse()
            ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error during user creation.")

    @GetMapping
    suspend fun findUsers(
        @RequestParam("name", required = false) name: String?
    ): Flow&lt;UserResponse> {
        val users = name?.let { userService.findAllUsersByNameLike(name) }
            ?: userService.findAllUsers()

        return users.map(User::toResponse)
    }

    @GetMapping("/{id}")
    suspend fun findUserById(
        @PathVariable id: Long
    ): UserResponse =
        userService.findUserById(id)
            ?.let(User::toResponse)
            ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "User with id $id was not found.")

    @DeleteMapping("/{id}")
    suspend fun deleteUserById(
        @PathVariable id: Long
    ) {
        userService.deleteUserById(id)
    }

    @PutMapping("/{id}")
    suspend fun updateUser(
        @PathVariable id: Long,
        @RequestBody userRequest: UserRequest
    ): UserResponse =
        userService.updateUser(
            id = id,
            requestedUser = userRequest.toModel()
        )
            .toResponse()
}

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

fun User.toResponse(): UserResponse =
    UserResponse(
        id = this.id!!,
        email = this.email,
        name = this.name,
        age = this.age
    )
</pre>



<p>I know, the class itself might be a little big, but let&#8217;s break it down into parts first. We have a couple of annotations here, so why don&#8217;t we start with them?</p>



<p>The <em>@RestController</em> is nothing else, then a combination of a <em>@Controller</em>&#8211; informing Spring that our class is a web controller and a <em>@ResponseBody-</em> which indicates that the things our functions return should be bound to the web response body (simply- returned to the API user).</p>



<p>The <em>@RequestMapping</em> allows us to specify the path, to which our class will respond. So, each time we will hit the <code class="EnlighterJSRAW" data-enlighter-language="raw">localhost:8080/api/users</code>, Spring will search for a handler function inside this class.</p>



<p>On the other hand, the <em>@PostMapping</em>, <em>@GetMapping</em>, etc. simply indicate for which HTTP methods a particular function should be invoked (and also can take the additional path segments).</p>



<p>Lastly, the <em>@RequestParam</em>, <em>@PathVariable</em>, and <em>@RequestBody</em> are used to map request parameters, segment paths, and body payload to Kotlin class instances.</p>



<p>The rest of the code is responsible for invoking our service methods and throwing meaningful errors when something is wrong (with a help of extension functions used to map between models and responses).</p>



<h3 class="wp-block-heading" id="h-8-3-implement-companyresponse-and-companyrequest">8.3. Implement CompanyResponse and CompanyRequest</h3>



<p>Similarly, let&#8217;s add response and request classes for Company resources:</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 CompanyRequest(
    val name: String,
    val address: String
)

data class CompanyResponse(
    val id: Long,
    val name: String,
    val address: String,
    val users: List&lt;UserResponse>
)
</pre>



<h3 class="wp-block-heading" id="h-8-4-add-companycontroller">8.4. Add CompanyController</h3>



<p>And this time, let&#8217;s add the <code class="EnlighterJSRAW" data-enlighter-language="raw">CompanyController</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="">@RestController
@RequestMapping("/api/companies")
class CompanyController(
    private val companyService: CompanyService,
    private val userService: UserService
) {

    @PostMapping
    suspend fun createCompany(@RequestBody companyRequest: CompanyRequest): CompanyResponse =
        companyService.saveCompany(
            company = companyRequest.toModel()
        )
            ?.toResponse()
            ?: throw ResponseStatusException(
                HttpStatus.INTERNAL_SERVER_ERROR,
                "Unexpected error during company creation."
            )

    @GetMapping
    suspend fun findCompany(
        @RequestParam("name", required = false) name: String?
    ): Flow&lt;CompanyResponse> {
        val companies = name?.let { companyService.findAllCompaniesByNameLike(name) }
            ?: companyService.findAllCompanies()

        return companies
            .map { company ->
                company.toResponse(
                    users = findCompanyUsers(company)
                )
            }
    }


    @GetMapping("/{id}")
    suspend fun findCompanyById(
        @PathVariable id: Long
    ): CompanyResponse =
        companyService.findCompanyById(id)
            ?.let { company ->
                company.toResponse(
                    users = findCompanyUsers(company)
                )
            }
            ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Company with id $id was not found.")

    @DeleteMapping("/{id}")
    suspend fun deleteCompanyById(
        @PathVariable id: Long
    ) {
        companyService.deleteCompanyById(id)
    }

    @PutMapping("/{id}")
    suspend fun updateCompany(
        @PathVariable id: Long,
        @RequestBody companyRequest: CompanyRequest
    ): CompanyResponse =
        companyService.updateCompany(
            id = id,
            requestedCompany = companyRequest.toModel()
        )
            .let { company ->
                company.toResponse(
                    users = findCompanyUsers(company)
                )
            }

    private suspend fun findCompanyUsers(company: Company) =
        userService.findUsersByCompanyId(company.id!!)
            .toList()
}


private fun CompanyRequest.toModel(): Company =
    Company(
        name = this.name,
        address = this.address
    )

private fun Company.toResponse(users: List&lt;User> = emptyList()): CompanyResponse =
    CompanyResponse(
        id = this.id!!,
        name = this.name,
        address = this.address,
        users = users.map(User::toResponse)
    )

</pre>



<p>And although this controller class looks similar, I wanted to emphasize two things:</p>



<ul class="wp-block-list">
<li>Firstly, thanks to coroutines we don&#8217;t have to do any sophisticated mapping, zipping, etc. (known from Reactor) in order to fetch users for each company,</li>



<li>and secondly- in order to edit Flow elements we use the map intermediate operator, which works just like a map when dealing with collections.</li>
</ul>



<h3 class="wp-block-heading" id="h-8-5-create-idnametyperesponse">8.5 Create IdNameTypeResponse</h3>



<p>As the last thing, I wanted to show you how easily we can merge two Flows. And to do so, let&#8217;s introduce a new search endpoint, which will be used to return both users and companies by their names.</p>



<p>So firstly, let&#8217;s add the <code class="EnlighterJSRAW" data-enlighter-language="raw">IdNameTypeResponse</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="">data class IdNameTypeResponse(
    val id: Long,
    val name: String,
    val type: ResultType
)

enum class ResultType {
    USER, COMPANY
}</pre>



<h3 class="wp-block-heading" id="h-8-6-add-searchcontroller">8.6 Add SearchController</h3>



<p>Moving forward, let&#8217;s implement the <code class="EnlighterJSRAW" data-enlighter-language="raw">SearchController</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="">@RestController
@RequestMapping("/api/search")
class SearchController(
  private val userService: UserService,
  private val companyService: CompanyService
) {

  @GetMapping
  suspend fun searchByNames(
    @RequestParam(name = "query") query: String
  ): Flow&lt;IdNameTypeResponse> {
    val usersFlow = userService.findAllUsersByNameLike(name = query)
      .map(User::toIdNameTypeResponse)
    val companiesFlow = companyService.findAllCompaniesByNameLike(name = query)
      .map(Company::toIdNameTypeResponse)

    return merge(usersFlow, companiesFlow)
  }
}

  private fun User.toIdNameTypeResponse(): IdNameTypeResponse =
    IdNameTypeResponse(
      id = this.id!!,
      name = this.name,
      type = ResultType.USER
    )

  private fun Company.toIdNameTypeResponse(): IdNameTypeResponse =
    IdNameTypeResponse(
      id = this.id!!,
      name = this.name,
      type = ResultType.COMPANY
    )</pre>



<p>As we can see, in order to combine together both user and company results we can use the <strong>merge</strong> function. This way, our flows are merged concurrently (and without preserving the order), without limit on the number of simultaneously collected flows.</p>



<h2 class="wp-block-heading" id="h-9-testing">9. Testing</h2>



<p>At this point, we have everything we need to start testing, so let it be your homework. Going through all of the handler methods and preparing appropriate requests will be a great opportunity to recap everything we learned today 🙂</p>



<p>As a bonus- right here you can find a ready-to-go <a href="https://drive.google.com/file/d/1xq3njZk2oGwHQGZkTFf05VwHr1pdsJJE/view?usp=sharing" target="_blank" rel="noopener">Postman collection</a>, which you can import to your computer.</p>



<h2 class="wp-block-heading" id="h-10-rest-api-with-spring-kotlin-coroutines-and-kotlin-flows-summary">10. REST API with Spring, Kotlin, coroutines, and Kotlin Flows Summary</h2>



<p>And that&#8217;s all for this hands-on tutorial, in which we&#8217;ve learned together how to create a REST API using Spring, Kotlin, coroutines, and Kotlin Flows. As always, you can find the whole project in this <a href="https://github.com/codersee-blog/spring-boot-3-kotlin-coroutines" target="_blank" rel="noopener">GitHub repository</a>.</p>



<p>If you&#8217;re interested in learning more about the reactive approach, then check out my other materials in the <a href="https://blog.codersee.com/tag/webflux/">Spring Webflux</a> tag.</p>



<p>I hope you enjoyed this article and will be forever thankful for your feedback in the comments section 🙂</p>
<p>The post <a href="https://blog.codersee.com/reactive-rest-api-with-spring-kotlin-and-coroutines/">Reactive REST API With Spring, Kotlin, and Coroutines</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/reactive-rest-api-with-spring-kotlin-and-coroutines/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>How To Follow Redirects (3XX) With Spring WebClient?</title>
		<link>https://blog.codersee.com/how-to-follow-redirects-3xx-with-spring-webclient/</link>
					<comments>https://blog.codersee.com/how-to-follow-redirects-3xx-with-spring-webclient/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 01 Nov 2022 06:00:21 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503816</guid>

					<description><![CDATA[<p>In this blog post, I would like to show you how to handle 3XX status codes properly and follow redirects with Spring WebClient.</p>
<p>The post <a href="https://blog.codersee.com/how-to-follow-redirects-3xx-with-spring-webclient/">How To Follow Redirects (3XX) With Spring WebClient?</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 friend! If you see this post, then you are either a regular visitor of my blog or you&#8217;ve already received your first 3XX response and realized that it&#8217;s time to <strong>follow redirects with Spring WebClient</strong> 😀</p>



<p>Regardless of the answer, I can assure you that you&#8217;ve come to the right place, and after reading this article you will know precisely how to implement a redirect feature with dedicated HttpClient functionality.</p>



<p>Before we start, I just want you to know that although the examples are written in Kotlin, you should be able to this logic regardless of the language used in your Spring Boot / WebFlux project.</p>



<h2 class="wp-block-heading" id="h-2-followredirect-a-simple-way-to-follow-redirects-with-spring-webclient">2. followRedirect() &#8211; a Simple Way to Follow Redirects with Spring WebClient</h2>



<p>So let&#8217;s start everything by introducing <em>HttpClient&#8217;s</em> method- <code class="EnlighterJSRAW" data-enlighter-language="generic">followRedirect()</code>&#8211; which we will make use of today. Of course, this is not the only solution to tackle the 3XX response case in Spring WebFlux, but in my opinion, it&#8217;s the cleanest way to do so. Moreover, this approach lets us implement our desired logic as a part of the configuration, without polluting the actual business logic.</p>



<p>When we look into the HttpClient implementation, we will spot that the <code class="EnlighterJSRAW" data-enlighter-language="generic">followRedirect()</code> method comes in <strong>6 variants</strong>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">HttpClient followRedirect(boolean)
HttpClient followRedirect(boolean, Consumer&lt;HttpClientRequest>)
HttpClient followRedirect(BiPredicate&lt;HttpClientRequest, HttpClientResponse>)
HttpClient followRedirect(boolean followRedirect, BiConsumer&lt;HttpHeaders, HttpClientRequest>)
HttpClient followRedirect(BiPredicate&lt;HttpClientRequest, HttpClientResponse>,BiConsumer&lt;HttpHeaders, HttpClientRequest>)
HttpClient followRedirect(BiPredicate&lt;HttpClientRequest, HttpClientResponse>, Consumer&lt;HttpClientRequest>)</pre>



<p>And although <strong>these signatures may look scary</strong>, you&#8217;ll see how simple to implement and useful they are in the upcoming paragraphs.</p>



<h2 class="wp-block-heading" id="h-3-set-up-the-project">3. Set Up The Project</h2>



<p>As always, let&#8217;s start by creating a new Spring Boot project using the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr</a>:</p>



<figure class="wp-block-image aligncenter"><img decoding="async" width="1494" height="728" src="http://blog.codersee.com/wp-content/uploads/2022/11/how-to-follow-redirects-3xx-with-spring-webclient-spring-initializr.png" alt="The image shows Spring Initializr page with all settings necessary to set up a new Spring WebFlux project." class="wp-image-5503824" srcset="https://blog.codersee.com/wp-content/uploads/2022/11/how-to-follow-redirects-3xx-with-spring-webclient-spring-initializr.png 1494w, https://blog.codersee.com/wp-content/uploads/2022/11/how-to-follow-redirects-3xx-with-spring-webclient-spring-initializr-300x146.png 300w, https://blog.codersee.com/wp-content/uploads/2022/11/how-to-follow-redirects-3xx-with-spring-webclient-spring-initializr-1024x499.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/11/how-to-follow-redirects-3xx-with-spring-webclient-spring-initializr-768x374.png 768w" sizes="(max-width: 1494px) 100vw, 1494px" /></figure>



<p>As we can see, the only additional dependency we will need today is the <strong>Spring Reactive Web</strong>. The rest is totally up to you.</p>



<p>Nextly, let&#8217;s generate this project and import it to our favorite IDE (like <em>IntelliJ</em>, for example).</p>



<h2 class="wp-block-heading" id="h-4-configure-webclient-and-httpclient">4. Configure WebClient and HttpClient</h2>



<p>As the next step to properly follow redirects with Spring WebClient, let&#8217;s add a <strong>Config</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="">@Configuration
class Config {

  companion object {
    private const val BASE_URL = "http://localhost:8090"
  }

  @Bean
  fun webClient(httpClient: HttpClient): WebClient =
    WebClient.builder()
      .clientConnector(ReactorClientHttpConnector(httpClient))
      .baseUrl(BASE_URL)
      .build()

  @Bean
  fun httpClient(): HttpClient =
    HttpClient.create()

}</pre>



<p>As we can see, we create here the most basic implementation of the WebClient with a base URL set as <code data-enlighter-language="raw" class="EnlighterJSRAW">http://localhost:8090</code>.</p>


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



<h2 class="wp-block-heading" id="h-5-create-a-service">5. Create a Service</h2>



<p>Nextly, let&#8217;s implement the RedirectService responsible for querying particular 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="">@Service
class RedirectService(
  private val webClient: WebClient
) {

  fun one(): Mono&lt;ResponseBodyDto> =
    webClient.get()
      .uri("/some-endpoint")
      .header("custom-header", "custom-header-value")
      .header("Authorization", "Bearer value")
      .retrieve()
      .bodyToMono(ResponseBodyDto::class.java)

  data class ResponseBodyDto(val message: String)
}</pre>



<p>As we can clearly see, with this implementation we query the <code class="EnlighterJSRAW" data-enlighter-language="raw">http://localhost:8090/some-endpoint</code> with a <strong>GET</strong> request passing additional <code class="EnlighterJSRAW" data-enlighter-language="raw">custom-header</code> and <code class="EnlighterJSRAW" data-enlighter-language="raw">Authorization</code> headers. The second one is crucial in this tutorial and you&#8217;ll know why in the upcoming paragraphs.</p>



<p>Additionally, we expect that the endpoint returns JSON with one item containing a message, which we deserialize into <strong>ResponseBodyDto</strong> and return as a <strong>Mono</strong>.</p>



<h2 class="wp-block-heading" id="h-6-implement-controller">6. Implement Controller</h2>



<p>As the next step, let&#8217;s add a <strong>TestController</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="">@RestController
@RequestMapping("/test")
class TestController(
private val redirectService: RedirectService
) {

  @GetMapping
  fun one(): Mono&lt;RedirectService.ResponseBodyDto> =
    redirectService.one()

}</pre>



<p>As we can see, we expose one GET endpoint, which we will use to test our functionality.</p>



<p>If everything will be working correctly, we should see the 200 OK response with the message when performing <strong>GET</strong> requests to <code class="EnlighterJSRAW" data-enlighter-language="raw">http://localhost:8080/test</code>.</p>



<h2 class="wp-block-heading" id="h-7-test-redirect-with-default-implementation">7. Test Redirect With Default Implementation</h2>



<p>Finally, let&#8217;s test our functionality. The case is pretty straightforward: each time we perform a <code class="EnlighterJSRAW" data-enlighter-language="raw">GET http://localhost:8090/some-endpoint</code> request, as a response, we get the <strong>301 Status Code</strong> <strong>without the response body</strong> and with <code class="EnlighterJSRAW" data-enlighter-language="raw">Location</code> header set to <code class="EnlighterJSRAW" data-enlighter-language="raw">http://localhost:8090/one-redirect-target.</code></p>



<p>On the other hand, the <code class="EnlighterJSRAW" data-enlighter-language="raw">http://localhost:8090/one-redirect-target</code> endpoint returns <strong>200 OK with the example message</strong>:</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 --location --request GET 'http://localhost:8080/test'

# Response

Status: 200 OK 
Response Body: Empty</pre>



<p>As a result, we received the 200 OK status with an empty response body- <strong>definitely not something we would like to see</strong>.</p>



<p>To be even more specific, our implementation neither throws an error nor returns the correct value- it ends ups with an empty Mono, which we do not handle.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>If you are interested in how to set up a &#8220;fake&#8221; api, just like the one above, then the answer is <strong>Mockoon</strong>. Let me know in the comments section if you would be interested in a tutorial about it 😉</p>
</blockquote>



<h2 class="wp-block-heading" id="h-8-make-use-of-the-followredirect-boolean">8. Make Use of the followRedirect(boolean)</h2>



<p>With that being done, let&#8217;s finally do something to follow the 301 response in our WebClient.</p>



<p>As the first one, let&#8217;s start with the <code class="EnlighterJSRAW" data-enlighter-language="generic">followRedirect(boolean)</code> variant:</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
fun httpClient(): HttpClient =
  HttpClient.create()
    .followRedirect(true)</pre>



<p>The only thing we have to do in this case is to pass the <strong>true</strong> to the <code class="EnlighterJSRAW" data-enlighter-language="generic">followRedirect(boolean)</code> method. Per the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Specifies whether HTTP status <strong>301|302|303|307|308</strong> auto-redirect support is enabled.</p>
</blockquote>



<p>So this time, our implementation <strong>will follow the redirect properly when any of the above status codes is returned</strong>. Moreover, if we check in Mockoon, we will see that <strong>both the custom and Authorization headers are present</strong>.</p>



<p>But here comes the catch- the sensitive headers:</p>



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



<li>Cookie,</li>



<li>Authorization,</li>



<li>Proxy-Authorization,</li>
</ul>



<p><strong>are only added when redirecting to the same domain! </strong>When testing locally, when we simply change the port of a redirected resource from 8090 to another, we will see that the Authorization header is missing.</p>



<h2 class="wp-block-heading" id="h-9-followredirect-add-sensitive-headers">9. followRedirect- Add Sensitive Headers</h2>



<p>OK, so what possibility do we have if we would like to handle redirects to different domains and re-add the same headers?</p>



<p>Well, if this is the only thing we would like to do, then let&#8217;s make use of the <code class="EnlighterJSRAW" data-enlighter-language="java">HttpClient followRedirect(boolean followRedirect, BiConsumer&lt;HttpHeaders, HttpClientRequest&gt;)</code> implementation:</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
fun httpClient(): HttpClient =
  HttpClient.create()
    .followRedirect(true) { headers, request ->
      request.headers(headers)
    }</pre>



<p>As we can see, this method lets us use the HTTP headers we sent in our first request.</p>



<p>We can simply add all of them, just like above, or only specific ones. Either way- <strong>please be cautious with this approach to not leak users&#8217; tokens</strong>.</p>



<h2 class="wp-block-heading" id="h-10-print-redirect-request-info">10. Print Redirect Request Info</h2>



<p>But what if we don&#8217;t necessarily want to access the headers and simply want to log the redirect request information, or add another header?</p>



<p>Well, in such a case I would recommend the <code class="EnlighterJSRAW" data-enlighter-language="java">HttpClient followRedirect(boolean, Consumer&lt;HttpClientRequest&gt;)</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="">@Bean
fun httpClient(): HttpClient =
  HttpClient.create()
    .followRedirect(true) { redirectRequest ->
      println("URI: ${redirectRequest.uri()}")
      println("Is follow redirect: ${redirectRequest.isFollowRedirect}")
      println("Redirected from: ${redirectRequest.redirectedFrom().firstOrNull()}")
      println("Resource URL: ${redirectRequest.resourceUrl()}")
      redirectRequest.addHeader("another-header", "another-value")
      println("Request headers: ${redirectRequest.requestHeaders()}")
    }</pre>



<p>This variant allows us to pass a <strong>HttpClientRequest Consumer</strong>. As we can see above the instance of HttpClientRequest contains useful info, like:</p>



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



<li>whether the request is following the redirect,</li>



<li>previous redirections array (as there might be multiple),</li>



<li>the URL we are going to query now,</li>



<li>all headers of the upcoming request.</li>
</ul>



<p>Additionally, with this one, we can set more headers- just like above.</p>



<p>If we run the test now, we should see something like that:</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="">URI: /one-redirect-target
Is follow redirect: true
Redirected from: http://localhost:8090/some-endpoint
Resource URL: http://localhost:8091/one-redirect-target
Request headers: DefaultHttpHeaders[user-agent: ReactorNetty/1.0.23, host: localhost:8091, accept: */*, custom-header: custom-header-value, another-header: another-value]</pre>



<h2 class="wp-block-heading" id="h-11-follow-redirect-only-when-conditions-are-met">11. Follow Redirect Only When Conditions Are Met</h2>



<p>Lastly, let&#8217;s see what can we do to add custom logic checking whether the request should be followed, or not.</p>



<p>As I&#8217;ve mentioned earlier, the default implementation enables auto-redirect for the following status codes: 301|302|303|307|308.</p>



<p>If we would like to change this behavior, then the <code class="EnlighterJSRAW" data-enlighter-language="generic">HttpClient followRedirect(BiPredicate&lt;HttpClientRequest, HttpClientResponse&gt;)</code> implementation is a great choice:</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
fun httpClient(): HttpClient =
  HttpClient.create()
    .followRedirect { clientRequest, clientResponse ->
      clientRequest.requestHeaders().contains("custom-header")
        &amp;&amp; clientResponse.status().code() == 301
    }</pre>



<p>As we can see, the request we will follow the redirect only when our request contains a <code class="EnlighterJSRAW" data-enlighter-language="raw">custom-header</code> header and the response status code is <strong>301</strong>.</p>



<h2 class="wp-block-heading" id="h-12-follow-redirects-with-spring-webclient">12. Follow Redirects With Spring WebClient</h2>



<p>And that would be all for this article on<strong> how to follow redirects (3xx) responses with Spring WebClient</strong>.</p>



<p>Together, we learned how to implement such functionality and make use of different implementations shipped with Spring. Please keep in mind that there are two more methods, which I haven&#8217;t covered because they basically combine the logic I have shown (but still, they may suit your use-case better).</p>



<p>Finally, if you think this content is useful, or I helped you, then<strong> please leave a comment</strong>&#8211; that helps me to reach an even bigger audience. And as always- you can find the GitHub repository with the source code <a href="https://github.com/codersee-blog/kotlin-spring-webflux-gradle-followredirect" target="_blank" rel="noopener">right here.</a></p>
<p>The post <a href="https://blog.codersee.com/how-to-follow-redirects-3xx-with-spring-webclient/">How To Follow Redirects (3XX) With Spring WebClient?</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/how-to-follow-redirects-3xx-with-spring-webclient/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>YouTube Course On REST API With Spring WebFlux and Kotlin</title>
		<link>https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/</link>
					<comments>https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Fri, 15 Jul 2022 05:25:10 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=3002981</guid>

					<description><![CDATA[<p>In this course, I will show you how to create a REST API using Spring WebFlux, PostgreSQL, and Kotlin and containerize it with Docker.</p>
<p>The post <a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/">YouTube Course On REST API With Spring WebFlux and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A couple of days ago, I&#8217;ve shared a new YouTube course on how to create a <strong>REST API With Spring WebFlux and Kotlin. </strong>This video series is a step-by-step tutorial on how to create a REST API, connect the application to <strong>PostgreSQL</strong>, and moreover, containerize it using <strong>Docker</strong>.</p>
<p>If you are not sure, whether this is something for you, then please check the introduction, which will help you to make the decision:</p>
<p><a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FY3FA2y_eakA%2Fhqdefault.jpg" alt="YouTube Video"></a></p>
<p>If you would like to see the course, then you can find all videos in <a href="https://youtube.com/playlist?list=PLvN8k8yxjoesNnd0C0t4s36bgVHUwJkIR" target="_blank" rel="noopener">this playlist</a>.</p>
<p>This is my first YouTube course and I will be forever thankful if you would like to <strong>share some feedback with me</strong>. You can do that in the comments section here, under the video, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>. I am constantly trying to improve the content quality, but that&#8217;s simply impossible without hearing from you and your perspective 🙂</p>
<p>Let me know what you think about this course and what topics would you like to see after Spring WebFlux.</p>
<p>Finally, I wanted to share that Codersee was announced as one of the <a href="https://blog.feedspot.com/kotlin_programming_blogs/" target="_blank" rel="noopener"><strong>Top 10 Kotlin Programming Blogs</strong></a><strong> </strong>on the web by FeedSpot. On this occasion, I would like to thank you, dear reader, for your input and for being a part of this journey. Thanks and see you in the near future! 🙂<br />
<a href="https://codersee.com/newsletter/"><img decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<p>The post <a href="https://blog.codersee.com/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/">YouTube Course On REST API With Spring WebFlux and Kotlin</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/youtube-course-on-rest-api-with-spring-webflux-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Flyway Migrations With Spring WebFlux (R2DBC)</title>
		<link>https://blog.codersee.com/flyway-spring-webflux/</link>
					<comments>https://blog.codersee.com/flyway-spring-webflux/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 31 May 2022 05:41:27 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Spring]]></category>
		<category><![CDATA[Flyway]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2502876</guid>

					<description><![CDATA[<p>In this article I will show you how to configure Flyway migrations when working with Spring WebFlux and Spring Data R2DBC.</p>
<p>The post <a href="https://blog.codersee.com/flyway-spring-webflux/">Flyway Migrations With Spring WebFlux (R2DBC)</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In this short article, I would like to show you how to configure <strong>Flyway</strong> when working with<strong> Spring WebFlux and Spring Data R2DBC</strong>.</p>
<p>In my <a href="https://blog.codersee.com/flyway-migrations-with-spring-boot/" target="_blank" rel="noopener">previous tutorial</a>, we&#8217;ve learned a lot about Flyway itself and how to set it up correctly, when working with Spring Boot. Nevertheless, if we would like to apply this knowledge when building a reactive web application with Spring WebFlux and Spring Data R2DBC, we would have to take a different approach. And that&#8217;s what we are going to focus on in this tutorial.</p>
<h2 class="article-heading-introduction">2. Imports</h2>
<h3 class="article-heading-introduction">2.1. Create Spring WebFlux With R2DBC Project</h3>
<p>As the first step, let&#8217;s create a skeleton project. We can do so by simply going to the <a href="https://start.spring.io/" target="_blank" rel="noopener">Spring Initializr</a> page and selecting the following dependencies:</p>
<ul>
<li>Spring Reactive Web</li>
<li>Spring Data R2DBC</li>
<li>PostgreSQL Driver</li>
</ul>
<p>After that, we should see the following in our <code class="EnlighterJSRAW" data-enlighter-language="kotlin">build.gradle.kts</code> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
runtimeOnly("org.postgresql:postgresql")
runtimeOnly("org.postgresql:r2dbc-postgresql")

// other dependencies</pre>
<h3 class="article-heading-introduction">2.2. Add Flyway Dependency</h3>
<p>Unfortunately, we can&#8217;t add the Flyway dependency to our Spring WebFlux project on the same page, so we have to do it manually:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">implementation("org.flywaydb:flyway-core:8.5.11")</pre>
<p>With that being done, we have to reload Gradle changes and wait till the additional dependency is fetched to our environment.</p>
<p>&nbsp;</p>
<p><a href="https://codersee.com/newsletter/"><img loading="lazy" decoding="async" class="aligncenter wp-image-3002956 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/05/join_newsletter-1024x576.png" alt="Image shows two ebooks people can get for free after joining newsletter" width="800" height="419" /></a></p>
<p>&nbsp;</p>
<h2 class="article-heading-introduction">3. Configure application.properties (or YAML) File</h2>
<p>As the next step, let&#8217;s head to the <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.properties</code> file (or <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.yaml</code> in my case) and configure database connection along with Flyway:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">spring:
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/
    username: postgres
    password: password
  flyway:
    url: jdbc:postgresql://localhost:5432/
    user: postgres
    password: password</pre>
<p>Please keep in mind that the values used for port, username, and password may be different in your case. Moreover, in the case of the Flyway connection, we have to use <strong>JDBC</strong>.</p>
<h2 class="article-heading-introduction">4. Implement Flyway Migration</h2>
<p>Nextly, let&#8217;s prepare an SQL file, which we would like to run as the first migration. Let&#8217;s call it <strong>V1__My_First_Migration.sql</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="sql">CREATE TABLE person(
  id SERIAL NOT NULL PRIMARY KEY,
  email TEXT NOT NULL
);</pre>
<p>As we can see, this simple script will create a new table called <code class="EnlighterJSRAW" data-enlighter-language="raw">person</code>. Please remember to put this file in the correct directory-<strong> resources/db/migration/</strong> in our case.</p>
<h2 class="article-heading-introduction">5. Add Flyway Configuration For R2DBC</h2>
<p>As the next step, let&#8217;s run our application to see if our Flyway migration is working. Technically, the app started successfully. Nevertheless, logs do not contain anything about applied migration and when we check the database we can clearly see, that <strong>migration was not run</strong>.</p>
<p>To change that, let&#8217;s add a <code class="EnlighterJSRAW" data-enlighter-language="kotlin">FlywayConfiguration</code> file to our project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Configuration
class FlywayConfiguration(private val env: Environment) {

    @Bean(initMethod = "migrate")
    fun flyway(): Flyway {
        return Flyway(Flyway.configure()
            .dataSource(
                env.getRequiredProperty("spring.flyway.url"),
                env.getRequiredProperty("spring.flyway.user"),
                env.getRequiredProperty("spring.flyway.password"))
        )
    }
}</pre>
<p>Alternatively, we can use <code class="EnlighterJSRAW" data-enlighter-language="kotlin">@Value</code> annotation, as well:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Configuration
class FlywayConfiguration(
    @Value("\${spring.flyway.url}") private val url: String,
    @Value("\${spring.flyway.user}") private val user: String,
    @Value("\${spring.flyway.password}") private val password: String
) {

    @Bean(initMethod = "migrate")
    fun flyway(): Flyway {
        return Flyway(
            Flyway.configure()
                .dataSource(url, user, password)
        )
    }
}</pre>
<p>As we can see, this configuration is responsible for creating a new Flyway bean with the URL, username, and password obtained from our <code class="EnlighterJSRAW" data-enlighter-language="kotlin">application.yaml</code> file.</p>
<p>If we rerun our app once again, we will see the following:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">Creating Schema History table "public"."flyway_schema_history" ...
Current version of schema "public": &lt;&lt; Empty Schema &gt;&gt;
Migrating schema "public" to version "1 - My First Migration"
Successfully applied 1 migration to schema "public", now at version v1 (execution time 00:00.034s)</pre>
<p>The above message clearly indicates that the migration was run and we can expect a new table in our database.</p>
<h2 class="article-heading-introduction">6. Summary</h2>
<p>And that would be all for this short article on how to configure <strong>Flyway migrations with Spring WebFlux and R2DBC</strong>.</p>
<p>Let me know if you found this material useful in the comment sections below, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener">contact form</a>.</p>
<p>Finally, if you would like to see the whole source code, please refer to this <a href="https://github.com/codersee-blog/spring-webflux-flyway" target="_blank" rel="noopener">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/flyway-spring-webflux/">Flyway Migrations With Spring WebFlux (R2DBC)</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/flyway-spring-webflux/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 2</title>
		<link>https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-2/</link>
					<comments>https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-2/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 21 Dec 2021 07:30:49 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Project Reactor]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=1809</guid>

					<description><![CDATA[<p>This time, we will focus on differences between just(), defer(), fromSupplier() and create() methods in exceptions and null handling.</p>
<p>The post <a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-2/">Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 2</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In the <a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/">previous article,</a> we&#8217;ve covered the basic differences between <strong>Mono.just()</strong>, <strong>defer()</strong>, <strong>fromSupplier()</strong> and <strong>create()</strong> methods. If you haven&#8217;t seen it yet, I highly recommend you to take a while and check it out.</p>
<p>Although we&#8217;ve spent quite some time trying to understand the behavior and possible use cases for each method, it&#8217;s worth to mention that we focused on successful cases. Unfortunately, this is not always the case in real-life scenarios. Provided that, in this article we will focus on how the mentioned methods vary in <strong>exceptions and null handling</strong> and what should we be aware of.</p>
<h2 class="article-heading-introduction">2. Imports</h2>
<p>As the first step, let&#8217;s import the necessary library to our project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation("io.projectreactor:reactor-core:3.4.12")
</pre>
<h2 class="article-heading-main">3. Null Handling</h2>
<p>After that, we can start can start practicing with nulls, also known as <em>The Billion Dollar Mistake</em>. Even though the Kotlin provides us a great support in null prevention, they can still occur- either on purpose (hopefully), or by mistake.</p>
<p>Given that, let&#8217;s prepare a function called <em>nullReturningDateFetching</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun nullReturningDateFetching(): LocalDateTime? {
    Thread.sleep(500)
    println("GETTING DATE")
    return null
}
</pre>
<p>As a word of explanation- we expect that the above code will cause the current thread to sleep, following by printing <em>GETTING DATE</em> and returning a null value.</p>
<h3 class="article-heading-sub">3.1. Mono.just()</h3>
<p>As the next step, let&#8217;s start with the <strong>Mono.just()</strong> method- just like in the previous article:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun monoJustNull(): Mono&lt;LocalDateTime&gt; =
    Mono.just(nullReturningDateFetching())

fun monoJustNullSubscription() {
    val myMono = monoJustNull()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}

fun monoJustNullInstantiation() {
    val myMono = monoJustNull()
}
</pre>
<p>And let&#8217;s run both methods<span class="VIiyi" lang="en"><span class="JLqJ4b ChMk0b" data-language-for-alternatives="en" data-language-to-translate-into="pl" data-phrase-index="0" data-number-of-phrases="1">:</span></span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">// monoJustNullSubscription
GETTING DATE
Exception in thread "main" java.lang.NullPointerException: value

// monoJustNullInstantiation
GETTING DATE
Exception in thread "main" java.lang.NullPointerException: value</pre>
<p>As we can clearly see, both cases completed with the same result- thrown <em>NullPointerException</em>. Whatsoever, if we used an IDE, like IntelliJ, it warned us about <strong>type mismatch</strong>! The reason behind that is pretty straightforward- internally, the <em>Mono.just()</em> method creates a <em>MonoJust</em> class instance, which requires a constructor argument to be <strong>not-null</strong>.</p>
<p>Additionally, we can&#8217;t implement any callback behavior using error-handling operators, like <em>onErrorReturn</em>(). The only possibility to recover in this case would be to wrap the <em>Mono.just(nullReturningDateFetching())</em> with a try-catch block and personally, I find this solution a bit ugly.</p>
<p>Thankfully, if we really need to use the <em>Mono.just()</em> and the value we would like to wrap can be null, a Mono class ships with the <strong>justOrEmpty() </strong>method. Instead of throwing exception, it will emit an <em>onComplete</em> signal, which we can fallback with, for instance, <em>defaultIfEmpty(</em>).</p>
<h3 class="article-heading-sub">3.2. Mono.defer()</h3>
<p>As the next example, let&#8217;s figure out how does the <strong>Mono.defer()</strong> works:</p>
<blockquote><p>Note: the following examples will focus on the subscription part- the reason for this has been covered in the previous article.</p></blockquote>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun monoDeferNull(): Mono&lt;LocalDateTime&gt; =
    Mono.defer { null }
 
fun monoDeferSubscription() {
    val myMono = monoDeferNull()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}
</pre>
<p>Nextly, let&#8217;s check the result:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">// monoDeferSubscription
// 3x:
[ERROR] (main) Operator called default onErrorDropped - reactor.core.Exceptions$ErrorCallbackNotImplemented: 
java.lang.NullPointerException: The Mono returned by the supplier is null
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.NullPointerException: The Mono returned by the supplier is null
Caused by: java.lang.NullPointerException: The Mono returned by the supplier is null
</pre>
<p>As we might have noticed, the exception seems to be exactly the same. However, it has been caught internally and propagated into the <em>onError</em> signal. For that reason, the number of messages is equal to the number of Subscribers we&#8217;ve created.</p>
<p>Let&#8217;s take a while to see what happens underneath:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">try {
    p = Objects.requireNonNull(supplier.get(),
            "The Mono returned by the supplier is null");
}
catch (Throwable e) {
    Operators.error(actual, Operators.onOperatorError(e, actual.currentContext()));
    return;
}
</pre>
<p>We can clearly spot, that this code is responsible for the message we&#8217;ve received. The <em>Operators.error</em> itself is responsible for calling the <em>onError</em> method of our Subscribers.</p>
<p>On the positive side, this behavior allows us to<span class="VIiyi" lang="en"> recover with some error-handling operator, like: </span></p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun monoDeferSubscriptionRecover() {
    val myMono =
            monoDeferNull()
                .onErrorReturn(LocalDateTime.MIN)

    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}
// Result:
-999999999-01-01T00:00
-999999999-01-01T00:00
-999999999-01-01T00:00
</pre>
<h3 class="article-heading-sub">3.3. Mono.fromSupplier()</h3>
<p>Nextly, let&#8217;s check the<strong> Mono.fromSupplier()</strong> method:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun monoFromSupplierNull(): Mono =
    Mono.fromSupplier { nullReturningDateFetching() }

fun monoFromSupplierSubscription() {
    val myMono = monoFromSupplierNull()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}
</pre>
<p>Similarly, let&#8217;s see the result:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">// monoFromSupplierSubscription
GETTING DATE
GETTING DATE
GETTING DATE
</pre>
<p>This time, no exception has been thrown. In practice, the <strong><em>fromSupplier()</em></strong> method just completes empty when the provided Supplier ( <em>nullReturningDateFetching()</em> in our case) resolves to null. Although we got rid of exception, we need to be aware of that and handle it accordingly. Indeed, the Mono class ships with <em>defaultIfEmpty</em> method, which could be used here<span class="VIiyi" lang="en">.</span></p>
<h3 class="article-heading-sub">3.3. Mono.create()</h3>
<p>As the last example of null handling, let&#8217;s see the <strong>Mono.create()</strong> behavior:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun monoCreateNull(): Mono =
    Mono.create { monoSink -&gt; monoSink.success(nullReturningDateFetching()) }

fun monoCreateNullSubscription() {
    val myMono = monoCreateNull()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}
</pre>
<p>And again, let&#8217;s run the above code:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">// monoCreateNullSubscription
GETTING DATE
GETTING DATE
GETTING DATE
</pre>
<p>As we can see, result itself is exactly the same, as in the previous example. However, the reason is slightly different and pretty well explained in the documentation itself:</p>
<blockquote><p>Calling this method [ success(T) ] with a null value will be silently accepted as a call to success() by standard implementations.</p></blockquote>
<h6></h6>
<p>As a result, the silently accepted <em>success()</em> call completes without any value.</p>
<h2 class="article-heading-main">3. Exceptions Handling</h2>
<p>With all of that being explained, we can finally focus on their differences in <strong>exceptions handling</strong>.</p>
<p>Just like previously, let&#8217;s start with another date fetching variant, called <em>exceptionDateFetching</em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun exceptionDateFetching(): LocalDateTime {
    Thread.sleep(500)
    println("GETTING DATE")
    throw RuntimeException("ERRORS HAPPEN")
}</pre>
<p>Unlike the previous paragraph, let&#8217;s perform a collective comparison of all methods:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun monoJustException(): Mono&lt;LocalDateTime&gt; =
Mono.just(exceptionDateFetching())

private fun monoDeferException(): Mono&lt;LocalDateTime&gt; =
    Mono.defer { monoJustException() }

private fun monoFromSupplierException(): Mono&lt;LocalDateTime&gt; =
    Mono.fromSupplier { exceptionDateFetching() }

private fun monoCreateException(): Mono&lt;LocalDateTime&gt; =
    Mono.create { monoSink -&gt; monoSink.success(exceptionDateFetching()) }</pre>
<p>After running all of them, we should see the following result:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">// monoJustExceptionSubscription
GETTING DATE
Exception in thread "main" java.lang.RuntimeException: ERRORS HAPPEN

// monoDeferExceptionSubscription &amp; monoFromSupplierExceptionSubscription &amp; monoCreateExceptionSubscription
// 3x
GETTING DATE
[ERROR] (main) Operator called default onErrorDropped - reactor.core.Exceptions$ErrorCallbackNotImplemented: 
java.lang.RuntimeException: ERRORS HAPPEN
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.RuntimeException: ERRORS HAPPEN
Caused by: java.lang.RuntimeException: ERRORS HAPPEN
</pre>
<p>As we can see- when using the <strong>Mono.just()</strong> method, we have to explicitly make sure that all exceptions are handled within the function responsible for date-time creation. Without that, the exception will be thrown on the instantiation and lead to thread termination.</p>
<p>On the other hand, the rest of the methods will log an error and finally, throw it via <em>Exceptions.bubble(Throwable)</em>. In other words, the exception will be again caught internally and propagated as an <em>onError</em> signal. As we have already seen, this type of signal might be pretty easily handled with onError* methods.</p>
<h2 class="article-heading-main">5. Summary</h2>
<p>And that would be all for this article. I really hope, that you will benefit from this, and the previous tutorial describing differences between the <strong>Mono.just(), defer(), fromSupplier() and create()</strong> methods. Although the Project Reactor is not the most trivial thing in the world, I am 100% sure that you can take advantage of learning it. It will be a pleasure to me to walk you through this process.</p>
<p>Finally, just like always, you can find the source code <a href="https://github.com/codersee-blog/kotlin-reactor-part-2" target="_blank" rel="noopener">here</a>. Moreover, if you would like to ask about anything or you have some suggestions about future topics, please let me know about it in the comment section below, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> form.</p>
<p>&nbsp;</p>
<h4 class="article-heading-sub">If you find this material useful, you might be interested in my previous articles:</h4>
<ul>
<li><a href="https://blog.codersee.com/what-is-kotlin-and-why-should-you-learn-it/">What is Kotlin and Why Should You Learn It?</a></li>
<li><a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/">Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 1</a></li>
<li><a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin/">Micronaut with MongoDB and Kotlin</a></li>
</ul>
<p>The post <a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-2/">Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 2</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/mono-just-defer-fromsupplier-create-part-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 1</title>
		<link>https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/</link>
					<comments>https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 14 Dec 2021 06:04:13 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Project Reactor]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=1796</guid>

					<description><![CDATA[<p>After reading this first article in a series, you will have a decent understanding of four ways of creating Monos in Project Reactor.</p>
<p>The post <a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/">Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 1</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 the first article of a series related to the <a href="https://projectreactor.io/">Project Reactor</a>, I would like to show you the process of creating Monos with <strong>Mono.just()</strong>, <strong>Mono.defer()</strong>, <strong>Mono.fromSupplier()</strong> and <strong>Mono.create()</strong> methods. After this step by step tutorial, you will have a<strong> decent understanding of each method and differences between them</strong>.</p>



<p>Nonetheless, this is not an introduction to the Project Reactor and to get the most out of this article, <span class="VIiyi" lang="en"><span class="JLqJ4b ChMk0b" data-language-for-alternatives="en" data-language-to-translate-into="pl" data-phrase-index="0" data-number-of-phrases="1">you need to understand at least the basic concepts associated with it. Considering that, I highly recommend their official docs, but if you would be interested in a video explanation, please let me know in the comments section below. </span></span></p>



<p>As the last thing in the introduction I would like welcome you to the Codersee after a long period of time. Last 12 months have been a pretty tough time for me and I had to suspend all of my blog activities. Nevertheless, I am back to blogging bringing you new portion of knowledge and ideas. I am so excited about upcoming months and really hope that you will be too.</p>



<h2 class="wp-block-heading" id="h-2-eager-vs-lazy-evaluation">2. Eager vs Lazy Evaluation</h2>



<p>Before we start considerations about Mono creation, let&#8217;s take a while to understand the difference between <strong>eager</strong> and <strong>lazy</strong> <strong>evaluation</strong>. In general, both terms describe the way expressions are evaluated. With <strong>eager evaluation</strong>, the value will be evaluated immediately, when the instruction has been encountered. In the second case, the process will be delayed until the value is really needed, hence the term- <strong>lazy evaluation</strong>.</p>


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



<h2 class="wp-block-heading" id="h-3-hot-vs-cold-publishers">3. Hot vs Cold Publishers</h2>



<p>Another terms I would like to cover before heading to the clue of this article are <strong>hot and cold publishers</strong>. In Project Reactor all Publishers (like Monos or Fluxes) are considered either one of them. <strong>Cold publishers</strong>, generate new data for each subscription (consider it a bit similar to lazy evaluation), whereas <strong>hot publishers</strong> are independent of subscribers and will produce data anyway (similarity to eagerness).</p>



<p>Undoubtedly, the two above paragraphs are just a brief summary and I highly recommend you do a further investigation. However, I am pretty sure that this knowledge will be sufficient for the purpose of this tutorial.</p>



<h2 class="wp-block-heading" id="h-4-imports">4. Imports</h2>



<p>With that being said, let&#8217;s start by importing necessary library to our project:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="groovy" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation("io.projectreactor:reactor-core:3.4.12")
</pre>



<p>Note: you can find the source code for this article, as always, in our <a href="https://github.com/codersee-blog/kotlin-reactor-part-1">GitHub repository</a>.</p>



<h2 class="wp-block-heading" id="h-5-create-monos">5. Create Monos</h2>



<p>After that, let&#8217;s define what exactly a <strong>Mono</strong> is in terms of Project Reactor? Well, it&#8217;s nothing else than just a specialized Publisher capable of asynchronous emitting of either 1 or 0 element. Fluxes, on the other hand, represent an asynchronous sequence of 0 to N elements.</p>



<h3 class="wp-block-heading" id="h-4-1-mono-just">4.1. Mono.just()</h3>



<p>Provided that, let&#8217;s start everything with an example function called <em><strong>successfulDateFetching</strong></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="">fun successfulDateFetching(): LocalDateTime {
    Thread.sleep(500)
    println("GETTING DATE")
    return LocalDateTime.now()
}
</pre>



<p>As we can see, it will be responsible for three things:</p>



<ul class="wp-block-list">
<li>causing the currently executing thread to sleep,</li>



<li>printing the text to the output,</li>



<li>and finally, returning the current date-time information from the system clock</li>
</ul>



<p>As the next step, let&#8217;s let&#8217;s implement the following <strong><code class="EnlighterJSRAW" data-enlighter-language="kotlin">Mono.just()</code></strong> 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="">fun monoJust(): Mono&lt;LocalDateTime> =
    Mono.just(successfulDateFetching())

fun monoJustSubscription() {
    val myMono = monoJust()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}

fun monoJustInstantiation() {
    val myMono = monoJust()
}
</pre>



<p>The above code is responsible for creating a new Mono inside the <strong><em>monoJust</em> </strong>function and after that it&#8217;s utilized inside the <strong>monoJustSubscription</strong> or <strong>monoJustInstantiation </strong>methods. Given that, it&#8217;s worth to quote the Reactor documentation here:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Nothing Happens Until You subscribe()</p>
</blockquote>



<p>And that&#8217;s one of the most important things when dealing with Reactor- data do not start pumping into by default. By creating a Mono, we&#8217;re just defining an asynchronous process, but all the magic happens when we tie it to a<strong> Subscriber</strong>. Personally, I would compare that to the process of <strong>class definition</strong>&#8211; just like a class is a simple skeleton with some predefined behavior until we create it&#8217;s <strong>instance</strong>, so here a Mono defines the process of data flow until we subscribe to it.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Note: As always, I highly encourage you to copy and run all of the examples manually, so that<span class="VIiyi" lang="en"><span class="JLqJ4b ChMk0b" data-language-for-alternatives="en" data-language-to-translate-into="pl" data-phrase-index="0" data-number-of-phrases="1"> you could get the most out of this article. </span></span></p>
</blockquote>



<p><span class="VIiyi" lang="en"><span class="JLqJ4b ChMk0b" data-language-for-alternatives="en" data-language-to-translate-into="pl" data-phrase-index="0" data-number-of-phrases="1">With that being said, we expect that that a date-time information will be printed 3 times after running <em>monoJustSubscription</em> and nothing happen in second scenario. Let&#8217;s validate if it&#8217;s true:</span></span></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="">// monoJustSubscription
GETTING DATE
2021-12-08T07:58:22.053724500
2021-12-08T07:58:22.053724500
2021-12-08T07:58:22.053724500

// monoJustInstantiation
GETTING DATE</pre>



<p>As you might have noticed- the result seems to be a bit odd. Why are the printed values exactly the same in the first case and why does the <em>GETTING DATE</em> has been even printed without subscription?</p>



<p>Basically, this is the main difference between a <strong>Mono.just()</strong> and the rest of the methods we&#8217;ll compare in this article. It is a<strong> hot publisher</strong> and the value has been captured at the <strong>instantiation time.</strong> In fact, the<em> successfulDateFetching()</em> has been invoked when we created and instance of Mono- even if we didn&#8217;t subscribe to it! Undeniably, we should be aware of this behavior especially when dealing with multiple subscribers.</p>



<h3 class="wp-block-heading" id="h-4-2-mono-defer">4.2. Mono.defer()</h3>



<p>Thankfully, <strong>Mono.just()</strong> is not the only possible way of creating Monos. Let&#8217;s see what can we do to delay the process of obtaining the date-time with <strong>Mono.defer()</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 monoDefer(): Mono&lt;LocalDateTime> =
    Mono.defer { monoJust() }

fun monoDeferSubscription() {
    val myMono = monoDefer()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}

fun monoDeferInstantiation() {
    val myMono = monoDefer()
}
</pre>



<p>After that, let&#8217;s run the examples and see the result:</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="">// monoDeferSubscription
GETTING DATE
2021-12-08T09:01:56.457742200
GETTING DATE
2021-12-08T09:01:56.970125600
GETTING DATE
2021-12-08T09:01:57.487761200

// monoDeferInstantiation 

</pre>



<p>This time, we&#8217;ve achieved a real laziness- a <em>successfulDateFetching()</em> was triggered each time a new Subscriber was registered. Moreover, nothing happened, when we just instantiated a Mono. The reason behind this is pretty straightforward- <em>Mono.defer()</em> will supply a target Mono (created by the <em>monoJust()</em>, in our case) to each Subscriber. In fact, the delay between each subscription could be extended to whatever value we would like to, and the printed date-time would be up to date each time.</p>



<p>At this point, we can clearly see that these two methods serve different purposes. If we are dealing with some constant data, or data set, or we are just OK with the data being obtained eagerly, then the <em>Mono.just()</em> should be the choice. On the other hand, if we want the subscriber to receive data calculated at the time of subscription, then the <em>Mono.defer()</em> should be picked to &#8220;wrap&#8221; another Mono.</p>



<h3 class="wp-block-heading" id="h-4-3-mono-fromsupplier">4.3. Mono.fromSupplier()</h3>



<p>Similarly to defer(), we can delay the data evaluation with <strong>Mono.fromSupplier()</strong> case. As the documentation states, it allows us to:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Create a <strong>Mono</strong>, producing its value using the provided <strong>Supplier</strong>. If the Supplier resolves to <strong>null</strong>, the resulting <strong>Mono completes empty</strong>.</p>
</blockquote>



<p>Given that&#8217;s let&#8217;s see the following 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="">private fun monoFromSupplier(): Mono&lt;LocalDateTime> =
    Mono.fromSupplier { successfulDateFetching() }

fun monoFromSupplierSubscription() {
    val myMono = monoFromSupplier()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}

fun monoFromSupplierInstantiation() {
    val myMono = monoFromSupplier()
}
</pre>



<p>In this case, the output presents as follows:</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="">// monoFromSupplierSubscription
GETTING DATE
2021-12-08T08:33:59.168426800
GETTING DATE
2021-12-08T08:33:59.671269400
GETTING DATE
2021-12-08T08:34:00.187244300

// monoFromSupplierInstantiation

</pre>



<p>We can clearly spot that this, and Mono.defer() execution worked exactly the same. As a word of explanation- both methods serve us to delay (defer) the moment of capturing the value. Most of the time, we ill lean toward them when dealing with external libraries, or another part of the code that we do not have an influence on. To put it simple, our choice will be:</p>



<ul class="wp-block-list">
<li><strong>Mono.defer()</strong>&#8211; when dealing with another library, method or whatever else returning a Mono instance</li>



<li><strong>Mono.fromSupplier()</strong>&#8211; when consuming a simple value being returned from external (not a Mono)</li>
</ul>



<p>Additionally, it&#8217;s worth mentioning that Project Reactor ships with another method called <strong>fromCallable()</strong>, which you might be interested in, as well (but considerations on Supplier and Callable usage won&#8217;t be a part of this article).</p>



<h3 class="wp-block-heading" id="h-4-3-mono-create">4.3. Mono.create()</h3>



<p>Lastly, let&#8217;s have a look at<strong> Mono.create()</strong>. Despite it&#8217;s simple name it is the most advanced way of creating Monos covered in this article. Again, I highly recommend you to check out the official documentation for more thorough exaplanation and examples.</p>



<p>As a word of clarification-<strong> Mono.create()</strong> allows us to deal with internal Mono signals through the <strong>MonoSink&lt;T&gt;</strong> wrapper&#8217;s <strong>create()</strong>, <strong>create(T)</strong> and <strong>error(Throwable)</strong> methods. Similarly, it creates a deffered emitter, I believe the result of the following example is just a formality:</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 monoCreate(): Mono =
    Mono.create { monoSink ->
        monoSink.success(successfulDateFetching())
    }

fun monoCreateSubscription() {
    val myMono = monoCreate()
    myMono.subscribe(::println)
    myMono.subscribe(::println)
    myMono.subscribe(::println)
}

fun monoCreateInstantiation() {
    val myMono = monoCreate()
}

// #Result
// monoCreateSubscription
// GETTING DATE 2021-12-08T08:33:59.168426800
// GETTING DATE 2021-12-08T08:33:59.671269400 
// GETTING DATE 2021-12-08T08:34:00.187244300 

// monoCreateInstantiation

</pre>



<p>As we might have noticed, the instantiation process is deferred here, just like when dealing with defer() and fromSupplier() methods. Nevertheless, it is the most advanced method presented in this tutorial which gives us much more control over emitted values and in most cases- it&#8217;s usage won&#8217;t be necessary. On the other hand, it might be a great choice when dealing with some callback-based APIs.</p>



<h2 class="wp-block-heading" id="h-5-conclusion-on-mono-just-vs-defer-vs-fromsupplier-vs-create">5. Conclusion on Mono.just() vs defer() vs fromSupplier() vs create()</h2>



<p>Finally, let&#8217;s have a quick recap on Mono.just() vs defer() vs fromSupplier() vs create():</p>



<ul class="wp-block-list">
<li><strong>Mono.just()</strong>&#8211; value captured at the time of instantiation, each Subscriber will receive the same value</li>



<li><strong>Mono.defer()</strong>&#8211; supplies a target Mono to each Subscriber, so the value will be obtained when subscribing</li>



<li><strong>Mono.fromSupplier()</strong>&#8211; produces a value using provided subscriber on subscribe</li>



<li><strong>Mono.create()</strong>&#8211; creates a deferred emitter, the most advanced method allowing to operate on MonoSink&lt;T&gt;</li>
</ul>



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



<p>And that’s all for the first article describing differences between Mono&#8217;s just(), defer(), fromSupplier() and create() methods. Additionally, in the next episode, we will focus on differences in null and exceptions handling.</p>



<p>Finally, I really hope that after this read you&#8217;ll get a better understanding of their behavior and use cases. For the source code, please refer to this <a href="https://github.com/codersee-blog/kotlin-reactor-part-1">GitHub repository</a>. Moreover, if you would like to ask about anything or need some more explanation, please do it in the comment section below, or by using the <a href="https://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> form.</p>
<p>The post <a href="https://blog.codersee.com/mono-just-defer-fromsupplier-create-part-1/">Mono.just() vs defer() vs fromSupplier() vs create() &#8211; Part 1</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/mono-just-defer-fromsupplier-create-part-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Reactive MongoDB REST API CRUD with Spring Boot and Kotlin</title>
		<link>https://blog.codersee.com/reactive-mongodb-rest-api-crud-with-spring-boot-and-kotlin/</link>
					<comments>https://blog.codersee.com/reactive-mongodb-rest-api-crud-with-spring-boot-and-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 14 Dec 2020 08:00:54 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<category><![CDATA[Spring WebFlux]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1708</guid>

					<description><![CDATA[<p>After finishing this tutorial, you will be able to create a simple Spring Boot REST API CRUD with Spring Data Reactive MongoDB.</p>
<p>The post <a href="https://blog.codersee.com/reactive-mongodb-rest-api-crud-with-spring-boot-and-kotlin/">Reactive MongoDB REST API CRUD with Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In today&#8217;s article, I would like to walk you step by step through the process of creating a <strong>reactive</strong> <strong>MongoDB REST API CRUD</strong> with Spring Boot and Kotlin<strong>. </strong></p>
<p>Reactive programming is becoming more and more popular nowadays and plenty of projects incorporated the <strong>Spring WebFlux</strong> framework, which provides support for creating non-blocking web applications. The reactive approach has plenty of advantages and I personally believe every programmer should know it, at least at a basic level. However, we need to keep in mind, that it is not the Golden Bullet and for some projects, the well-known <strong>Spring Web MVC</strong> will be a better choice.</p>
<p>After finishing this tutorial, you will be able to create a simple Spring Boot REST API CRUD and connect it to the MongoDB with <strong>Spring Data Reactive MongoDB </strong>(if you&#8217;d like to see how to connect to the MongoDB in a standard Web MVC application, please see my <a href="http://codersee.com/spring-boot-mongodb-rest-api-crud-with-kotlin/">previous article</a>). Additionally, if this is your first meeting with the WebFlux or Project Reactor, I highly encourage you to check out this <a href="https://speakerdeck.com/simonbasle/flight-of-the-flux">article</a> covering the basics.</p>
<h2 class="article-heading-main">2. Run MongoDB Server</h2>
<p>Just like in the previous article, we need to make sure, that our <strong>MongoDB server is up and running</strong>. For simplicity, we will deploy it as a <strong>docker container</strong>. Nevertheless, if you would like to install a MongoDB on your local machine, please refer to <a href="https://docs.mongodb.com/manual/installation/" target="_blank" rel="noopener noreferrer">this official manual</a>.</p>
<h3 class="article-heading-sub">2.1. Deploy MongoDB as a Container</h3>
<p>Firstly, let&#8217;s pull the mongo image from the <strong>Docker Hub</strong> registry:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker pull mongo
</pre>
<p>After that, let&#8217;s deploy it in a detached mode (<em> -d</em> flag):</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker run -d -p 27017-27019:27017-27019 --name mongodb mongo
</pre>
<p>Besides deploying in a detached mode, the above command publishes the container&#8217;s ports to the host- in simple terms, we will be able to connect to the MongoDB using <em>localhost</em>.</p>
<p>Finally, let&#8217;s check if everything is OK with the following command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker ps

#command output
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                  NAMES
26bd5c8dcc79        mongo               "docker-entrypoint.s…"   6 seconds ago       Up 5 seconds        0.0.0.0:27017-27019-&gt;27017-27019/tcp   mongodb
</pre>
<h2 class="article-heading-main">3. Imports</h2>
<p>As the next step, let&#8217;s create a Spring Boot project and add necessary imports (I highly encourage you to use the <a href="http://start.spring.io/" target="_blank" rel="noopener noreferrer">Spring Initializr</a> when doing that):</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
testImplementation("io.projectreactor:reactor-test")
</pre>
<h2 class="article-heading-main">4. Configure Application Properties</h2>
<p>After we&#8217;ve configured the imports, let&#8217;s add an additional configuration to the <strong>application.yaml</strong> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">server:
  error:
    include-message: always
</pre>
<p>Since the<strong> Spring Boot 2.3</strong> release, <strong>error messages are no longer included</strong> in the server&#8217;s responses by default. In this guide, I would like to show you how to create a custom exception class with a message, so these 3 lines are necessary.</p>
<h2 class="article-heading-main">5. Create Models</h2>
<p>After all of the above being done, we can start defining models, which will be used to persist and fetch objects from the database. In our example, we will define two separate classes- a <strong>Company</strong> and an <strong>Employee</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Document("companies")
data class Company(
    @Id
    val id: String? = null,
    var name: String,
    @Field("company_address")
    var address: String
)
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">data class Employee(
    @Id
    val id: ObjectId? = null,
    var firstName: String,
    var lastName: String,
    var email: String,
    var company: Company?
)
</pre>
<p>As you might have noticed, the <strong>Company</strong> class definition has two (optional) annotations more, than the <strong>Employee</strong>. Let me explain them:</p>
<ul>
<li>a <em><strong>@Document</strong></em> annotation- usually, we will use it to change the default name of the MongoDB collection used to store the document. By default, the name of the collection will be the class name changed to start with a lower-case letter. So, in our database, we will have two collections created: the <em>companies</em> and the <em>employee</em>.</li>
<li>a <em><strong>@Field</strong></em> annotation- it&#8217;s used to define custom metadata for the document fields, like a key name, order, or a target type. In our example, we&#8217;ve changed the key name from the default <em>&#8220;address&#8221;</em> to the <em>&#8220;company_address&#8221;</em>.</li>
</ul>
<h3 class="article-heading-sub">5.1. What is ObjectId?</h3>
<p>You might be wondering, what exactly the <strong>ObjectId</strong> is? Well, by default MongoDB uses <strong>ObjectIds</strong> as the default value of the _id field of each document. An <b>ObjectId</b> is a 12-byte BSON type having the following structure:</p>
<ul class="list">
<li>The first 4 bytes represent the seconds since the Unix epoch</li>
<li>The next 3 bytes are the machine identifier</li>
<li>The next 2 bytes consists of process id</li>
<li>The last 3 bytes are a random counter value</li>
</ul>
<p>These 12 bytes altogether uniquely identify a document within the MongoDB collection and serve as a primary key for that document as well. From the Spring Boot perspective, we can treat them as <strong> ObjectIds</strong> or <strong>Strings</strong>&#8211; the decision is up to you.</p>
<h2 class="article-heading-main">6. Add Custom Exception</h2>
<p>Nextly, let&#8217;s create a custom exception class named <strong>NotFoundException</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@ResponseStatus(HttpStatus.NOT_FOUND)
class NotFoundException(msg: String) : RuntimeException(msg)
</pre>
<p>The <em><strong>@ResponseStatus</strong></em> annotation allows us to specify the status code to use for the response- in this case- <em>404 Not Found</em>.</p>
<h2 class="article-heading-main">7. Create Repositories</h2>
<p>As the next step, let&#8217;s create repositories for our data. Let&#8217;s start by adding the <strong>CompanyRepository</strong> first:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">interface CompanyRepository : ReactiveMongoRepository&lt;Company, String&gt;</pre>
<p>As you can see, the <strong>CompanyRepository</strong> interface extends the <strong>ReactiveMongoRepository</strong>, which provides us with basic <strong>CRUD</strong> operations (and of course a reactive support, as well). Similarly, let&#8217;s create the <strong>EmployeeRepository</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">interface EmployeeRepository : ReactiveMongoRepository&lt;Employee, ObjectId&gt; {
    fun findByCompanyId(id: String): Flux&lt;Employee&gt;
}</pre>
<p>This time, we&#8217;ve provided additionally the definition of <em><strong>findByCompanyId</strong></em> function, which will be used later to find employees of a specific company.</p>
<p>If you have any prior experience with Spring Data repositories, you might have noticed the crucial difference between the standard and reactive repositories- they are using Fluxes and Monos, instead of Objects and Collections.</p>
<h2 class="article-heading-main">8. Create Services</h2>
<h3 class="article-heading-sub">8.1. Implement CompanyService</h3>
<p>Before we will create the service, let&#8217;s add the <strong>CompanyRequest</strong> class, which will be used to store the data passed by the user:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class CompanyRequest(
    val name: String,
    val address: String
)
</pre>
<p>Nextly, let&#8217;s create the <strong>CompanyService</strong> class. Please don&#8217;t worry, if anything is confusing to you, I&#8217;ll cover all functions later.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Service
class CompanyService(
    private val companyRepository: CompanyRepository,
    private val employeeRepository: EmployeeRepository
) {

    fun createCompany(request: CompanyRequest): Mono&lt;Company&gt; =
        companyRepository.save(
            Company(
                name = request.name,
                address = request.address
            )
        )

    fun findAll(): Flux&lt;Company&gt; =
        companyRepository.findAll()

    fun findById(id: String): Mono&lt;Company&gt; =
        companyRepository.findById(id)
            .switchIfEmpty {
                Mono.error(
                    NotFoundException("Company with id $id not found")
                )
            }

    fun updateCompany(id: String, request: CompanyRequest): Mono&lt;Company&gt; =
        findById(id)
            .flatMap {
                companyRepository.save(
                    it.apply {
                        name = request.name
                        address = request.address
                    }
                )
            }
            .doOnSuccess { updateCompanyEmployees(it).subscribe() }

    fun deleteById(id: String): Mono&lt;Void&gt; =
        findById(id)
            .flatMap(companyRepository::delete)

    private fun updateCompanyEmployees(updatedCompany: Company): Flux&lt;Employee&gt; =
        employeeRepository.saveAll(
            employeeRepository.findByCompanyId(updatedCompany.id!!)
                .map { it.apply { company = updatedCompany } }
        )
}
</pre>
<p>Let&#8217;s start with the two easiest functions:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun createCompany(request: CompanyRequest): Mono&lt;Company&gt; =
    companyRepository.save(
        Company(
            name = request.name,
            address = request.address
        )
    )

fun findAll(): Flux&lt;Company&gt; =
    companyRepository.findAll()
</pre>
<p>They are responsible for creating and querying all companies, but instead of returning elements, they wrap them into Mono and Flux, which can be processed later.<br />
What about a <em><strong>findById</strong></em> function?</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findById(id: String): Mono&lt;Company&gt; =
    companyRepository.findById(id)
        .switchIfEmpty {
            Mono.error(
                NotFoundException("Company with id $id not found")
            )
        }</pre>
<p>The findById function from <strong>CompanyRepository</strong> returns a Mono with the existing data or an empty Mono in case if no data has been found. You might think about it as something similar to the Optional in Java. So in our case, if we would like to &#8220;throw&#8221; the exception, we need to create a new Mono instance with our Throwable- the NotFoundException object and pass it to the <em><strong>switchIfEmpty</strong></em> function.</p>
<p>Nextly, let&#8217;s check the <em><strong>updateCompany</strong></em> function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun updateCompany(id: String, request: CompanyRequest): Mono&lt;Company&gt; =
        findById(id)
            .flatMap {
                companyRepository.save(
                    it.apply {
                        name = request.name
                        address = request.address
                    }
                )
            }
            .doOnSuccess { updateCompanyEmployees(it).subscribe() }

private fun updateCompanyEmployees(updatedCompany: Company): Flux&lt;Employee&gt; =
    employeeRepository.saveAll(
        employeeRepository.findByCompanyId(updatedCompany.id!!)
            .map { it.apply { company = updatedCompany } }
    )
</pre>
<p>Firstly, it invokes the <em><strong>findById</strong></em> function to get the Mono object of the desired company. After that, we use the <em><strong>flatMap</strong></em> to transform the value emitted by it (the company) asynchronously and return the value emitted by the <em><strong>save</strong></em> method. If everything executed successfully, the <em><strong>updateCompanyEmployees</strong></em> function will be invoked. As the last step, we need to subscribe to the Flux returned from this function.</p>
<h3 class="article-heading-sub">8.2. MongoDB Relations</h3>
<p>You might be wondering <strong>why do we need to update all employees connected with the company</strong>, each time we are updating it? That&#8217;s because we keep the related company in denormalized form- inside the employee document. To visualize that, let&#8217;s check the example document from our database:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="json">{
    "firstName": "Piotr",
    "lastName": "Wolak",
    "email": "contact@codersee.com",
    "company": {
        "_id": {
            "$oid": "5fcb90f830e3af4497f5de14"
        },
        "name": "Company Two",
        "company_address": "Address Two"
    },
    "_class": "com.codersee.mongocrud.model.Employee"
}</pre>
<p>As you can see, the company document is stored as an inner document of each employee. This might seem pretty weird, especially when you have any previous experience working with SQL databases, but this is the optimal choice for most real-life scenarios when using MongoDB (if you would like to learn more about MongoDB relations, please let me know and I will create another tutorial about it :).</p>
<p>&nbsp;</p>
<p>As the last step, let&#8217;s check the <em><strong>deleteById</strong></em> function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun deleteById(id: String): Mono&lt;Void&gt; =
    findById(id)
        .flatMap(companyRepository::delete)</pre>
<p>All it does is to fetch the company&#8217;s Mono and delete its value returning the instance of <strong>Mono&lt;Void&gt;</strong>.</p>
<h3 class="article-heading-sub">8.3. Create EmployeeService</h3>
<p>Similarly, let&#8217;s create the <strong>EmployeeRequest</strong> responsible for data handling and <strong>EmployeeService</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class EmployeeRequest(
    val firstName: String,
    val lastName: String,
    val email: String,
    val companyId: String?
)
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Service
class EmployeeService(
    private val companyService: CompanyService,
    private val employeeRepository: EmployeeRepository
) {

    fun createEmployee(request: EmployeeRequest): Mono&lt;Employee&gt; {
        val companyId = request.companyId

        return if (companyId == null) {
            createEmployeeWithoutCompany(request)
        } else {
            createEmployeeWithCompany(companyId, request)
        }
    }

    private fun createEmployeeWithoutCompany(request: EmployeeRequest): Mono&lt;Employee&gt; {
        return employeeRepository.save(
            Employee(
                firstName = request.firstName,
                lastName = request.lastName,
                email = request.email,
                company = null
            )
        )
    }

    private fun createEmployeeWithCompany(companyId: String, request: EmployeeRequest) =
        companyService.findById(companyId)
            .flatMap {
                employeeRepository.save(
                    Employee(
                        firstName = request.firstName,
                        lastName = request.lastName,
                        email = request.email,
                        company = it
                    )
                )
            }

    fun findAll(): Flux&lt;Employee&gt; =
        employeeRepository.findAll()

    fun findAllByCompanyId(id: String): Flux&lt;Employee&gt; =
        employeeRepository.findByCompanyId(id)

    fun findById(id: ObjectId): Mono&lt;Employee&gt; =
        employeeRepository.findById(id)
            .switchIfEmpty {
                Mono.error(
                    NotFoundException("Employee with id $id not found")
                )
            }

    fun updateEmployee(id: ObjectId, request: EmployeeRequest): Mono&lt;Employee&gt; {
        val employeeToUpdate = findById(id)

        val companyId = request.companyId
        return if (companyId == null) {
            updateEmployeeWithoutCompany(employeeToUpdate, request)
        } else {
            updateEmployeeWithCompany(companyId, employeeToUpdate, request)
        }
    }

    private fun updateEmployeeWithoutCompany(employeeToUpdate: Mono, request: EmployeeRequest) =
        employeeToUpdate.flatMap {
            employeeRepository.save(
                it.apply {
                    firstName = request.firstName
                    lastName = request.lastName
                    email = request.email
                    company = null
                }
            )
        }

    private fun updateEmployeeWithCompany(
        companyId: String,
        employeeToUpdate: Mono,
        request: EmployeeRequest
    ) =
        companyService.findById(companyId)
            .zipWith(employeeToUpdate)
            .flatMap {
                employeeRepository.save(
                    it.t2.apply {
                        firstName = request.firstName
                        lastName = request.lastName
                        email = request.email
                        company = it.t1
                    }
                )
            }

    fun deleteById(id: ObjectId): Mono&lt;Employee&gt; {
        return findById(id)
            .flatMap(employeeRepository::delete)
    }
}
</pre>
<p>After implementing the previous chapter, most of the concepts used in the code above should be familiar to you, so I won&#8217;t be diving into the details here. However, you might be interested in what exactly the <em><strong>zipWith</strong></em> function does inside the <em><strong>updateEmployeeWithCompany</strong></em>?</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun updateEmployeeWithCompany(
    companyId: String,
    employeeToUpdate: Mono&lt;Employee&gt;,
    request: EmployeeRequest
) =
    companyService.findById(companyId)
        .zipWith(employeeToUpdate)
        .flatMap {
            employeeRepository.save(
                it.t2.apply {
                    firstName = request.firstName
                    lastName = request.lastName
                    email = request.email
                    company = it.t1
                }
            )
        }</pre>
<p>Well, the <em><strong>zipWith</strong></em> function combines the result of two Monos into a <strong>Tuple2</strong> (a data structure holding two, non-null values). In our case, firstly, we obtain the Company Mono from the CompanyService and then couple it with Employee Mono passed to the function. After this operation, our tuple instance is passed to the flatMap function allowing us to reference the Employee <em>(t2)</em> and Company<em> (t1)</em> instances.</p>
<h2 class="article-heading-main">9. Implement REST Controllers</h2>
<p>As the last step, we will implement the controller layer responsible for request handling. But before that, let&#8217;s prepare two classes: <strong>CompanyResponse</strong> and <strong>EmployeeResponse</strong> responsible for returning the data to the user:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class CompanyResponse(
    val id: String,
    val name: String,
    val address: String
) {
    companion object {
        fun fromEntity(company: Company): CompanyResponse =
            CompanyResponse(
                id = company.id!!,
                name = company.name,
                address = company.address
            )
    }
}
</pre>
<p><strong>Important note:</strong> although the creation of separate classes for transferring the incoming and outgoing data adds some redundancy to the code, I personally believe it is a good way to separate these concepts and make the code cleaner.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class EmployeeResponse(
    val id: String,
    val firstName: String,
    val lastName: String,
    val email: String,
    val company: CompanyResponse?
) {
    companion object {
        fun fromEntity(employee: Employee): EmployeeResponse =
            EmployeeResponse(
                id = employee.id!!.toHexString(),
                firstName = employee.firstName,
                lastName = employee.lastName,
                email = employee.email,
                company = employee.company?.let { CompanyResponse.fromEntity(it) }
            )
    }
}
</pre>
<p>As you can see, both classes contain <em><strong>fromEntity</strong></em> functions inside companion objects. As the name suggests, they will be used to convert entity objects to response instances.</p>
<h3 class="article-heading-sub">9.1. Create CompanyController</h3>
<p>With that being done, let&#8217;s add the <strong>CompanyController</strong> class to our project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@RestController
@RequestMapping("/api/company")
class CompanyController(
    private val companyService: CompanyService
) {

    @PostMapping
    fun createCompany(@RequestBody request: CompanyRequest): Mono&lt;CompanyResponse&gt; {
        return companyService.createCompany(request)
            .map { CompanyResponse.fromEntity(it) }
    }

    @GetMapping
    fun findAllCompanies(): Flux&lt;CompanyResponse&gt; {
        return companyService.findAll()
            .map { CompanyResponse.fromEntity(it) }
            .delayElements(Duration.ofSeconds(2))
    }

    @GetMapping("/{id}")
    fun findCompanyById(@PathVariable id: String): Mono&lt;CompanyResponse&gt; {
        return companyService.findById(id)
            .map { CompanyResponse.fromEntity(it) }
    }

    @PutMapping("/{id}")
    fun updateCompany(
        @PathVariable id: String,
        @RequestBody request: CompanyRequest
    ): Mono&lt;CompanyResponse&gt; {
        return companyService.updateCompany(id, request)
            .map { CompanyResponse.fromEntity(it) }
    }

    @DeleteMapping("/{id}")
    fun deleteCompany(@PathVariable id: String): Mono&lt;Void&gt; {
        return companyService.deleteById(id)
    }
}
</pre>
<p>You might have noticed the usage of the <em><strong>delayElements</strong></em> function. It adds the pause between emitting each element from our Flux instance. I&#8217;ve added it to simulate the additional delay, which might happen in real-life scenarios (for instance, when processing the data, or calling other services).</p>
<p>Additionally, all functions return either the Flux or Mono instance,<strong> but why do we do that?</strong></p>
<p>Well, with this approach, the behavior of the WebFlux will be dependent on the type of the incoming request:</p>
<ul>
<li>if we call the endpoint without specifying the <strong>Accept</strong> header, or when we set it to <strong>application/json</strong>, the request will be handled in a standard, blocking way returning a JSON in response</li>
<li>however, if we set the <strong>Accept</strong> header value to the <strong>text/event-stream</strong>,  then the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" target="_blank" rel="noopener noreferrer">Server-Sent Event</a> channel will be opened allowing the server to push the data to the client (in our case- with 2 seconds of the delay between each company response)</li>
</ul>
<h3 class="article-heading-sub">9.2. Create EmployeeController</h3>
<p>Similarly, let&#8217;s implement the <strong>EmployeeController. </strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin"> 
@RestController
@RequestMapping("/api/employee")
class EmployeeController(
    private val employeeService: EmployeeService
) {

    @PostMapping
    fun createEmployee(@RequestBody request: EmployeeRequest): Mono&lt;EmployeeResponse&gt; {
        return employeeService.createEmployee(request)
            .map { EmployeeResponse.fromEntity(it) }
    }

    @GetMapping
    fun findAllEmployees(): Flux&lt;EmployeeResponse&gt; {
        return employeeService.findAll()
            .map { EmployeeResponse.fromEntity(it) }
    }

    @GetMapping("/{id}")
    fun findEmployeeById(@PathVariable id: ObjectId): Mono&lt;EmployeeResponse&gt; {
        return employeeService.findById(id)
            .map { EmployeeResponse.fromEntity(it) }
    }

    @GetMapping("/company/{companyId}")
    fun findAllByCompanyId(@PathVariable companyId: String): Flux&lt;EmployeeResponse&gt; {
        return employeeService.findAllByCompanyId(companyId)
            .map { EmployeeResponse.fromEntity(it) }
    }

    @PutMapping("/{id}")
    fun updateUpdateEmployee(
        @PathVariable id: ObjectId,
        @RequestBody request: EmployeeRequest
    ): Mono&lt;EmployeeResponse&gt; {
        return employeeService.updateEmployee(id, request)
            .map { EmployeeResponse.fromEntity(it) }
    }

    @DeleteMapping("/{id}")
    fun deleteEmployee(@PathVariable id: ObjectId): Mono&lt;Void&gt; {
        return employeeService.deleteById(id)
    }
}
</pre>
<h2 class="article-heading-main">10. Test with CURL</h2>
<p>Finally, we can run our application and test the endpoints using cURL. If you would like to explore the database, I highly recommend the <a href="https://www.mongodb.com/products/compass" target="_blank" rel="noopener noreferrer">MongoDB Compass</a>, which is the dedicated GUI for MongoDB.</p>
<p>Let&#8217;s start by adding two companies:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw"># 1st company
curl --location --request POST 'localhost:8080/api/company' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "Company 1",
    "address": "Address 1"
}'

# 2nd company
curl --location --request POST 'localhost:8080/api/company' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "Company 2",
    "address": "Address 2"
}'
</pre>
<p>Nextly, let&#8217;s validate if the data has been added correctly in two ways. Let&#8217;s check out the <strong>blocking approach </strong>first:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl 'localhost:8080/api/company'

# Example output
[
  {
    "id": "5fcba07a30e3af4497f5de16",
    "name": "Company 1",
    "address": "Address 1"
  },
  {
    "id": "5fcba07c30e3af4497f5de17",
    "name": "Company 2",
    "address": "Address 2"
  }
]
</pre>
<p>As the next step, let&#8217;s test the endpoint utilizing the <strong>Server-Sent Event</strong> channel:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl http://localhost:8080/api/company -H "Accept: text/event-stream"

# Example output
data:{"id":"5fcba07a30e3af4497f5de16","name":"Company 1","address":"Address 1"}

data:{"id":"5fcba07c30e3af4497f5de17","name":"Company 2","address":"Address 2"}
</pre>
<p>In the first case, we had to wait around 4 seconds for the server response (2 companies * 2 seconds of the delay). On the other hand, utilizing the SSE channel in the second case allows the server to push the data to the client chunk by chunk, with the delay between each entry.</p>
<p>Awesome, isn&#8217;t it? Let&#8217;s imagine that our endpoint returns thousands of data or the computation takes much longer. With the blocking request, we&#8217;d have to wait until everything will be processed to consume it. With the SSE, the client can process the data (for instance, displaying it immediately to the user) as soon, as it receives the first chunk.</p>
<p>After that, let&#8217;s validate if other endpoints are working as well. Let&#8217;s start by querying a specific company by the ID:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request GET 'localhost:8080/api/company/5fcba07c30e3af4497f5de17'

# Expected output
{
  "id": "5fcba07c30e3af4497f5de17",
  "name": "Company 2",
  "address": "Address 2"
}
</pre>
<p>To update the company, let&#8217;s use the PUT request:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request PUT 'localhost:8080/api/company/5fcba07c30e3af4497f5de17' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "Company 2 Updated",
    "address": "Address 2 Updated"
}'
</pre>
<p>If everything went well, we should see that our company data has been updated- you can always check it with another endpoint as well.</p>
<p>To delete the company, let&#8217;s use the DELETE handling endpoint:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request DELETE 'localhost:8080/api/company/5fcba07c30e3af4497f5de17'
</pre>
<p>This time, we should receive an empty response.</p>
<p>Similarly, we can test the rest of the endpoints responsible for employees management- let it be homework for you- I&#8217;ve always believed there is no other way to learn anything, than trying on our own 🙂</p>
<h2 class="article-heading-main">11. Summary</h2>
<p>And that&#8217;s all for this article 🙂 We&#8217;ve gone step by step through the process of deploying the <strong>MongoDB</strong> and a <strong>Docker</strong> container and creating the <strong>Reactive MongoDB REST API CRUD with Spring Boot and Kotlin.</strong></p>
<p>Please don&#8217;t worry, if some of the topics covered in this article seem to be difficult or unclear. The WebFlux and reactive approach, in general, are really complex topics and they require some time and practice to fully understand them. If you would like to ask about anything or need some explanation, please do it in the comment section below, or by using the <a href="http://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> form.</p>
<p>Traditionally, you can find the working project in this <a href="https://github.com/codersee-blog/kotlin-spring-boot-reactive-mongodb-crud" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>Finally, if you found this article useful, then you may want to check out how to generate <a href="https://blog.codersee.com/jacoco-with-spring-boot-gradle-and-kotlin/">test reports with Jacoco</a>.</p>
<p>The post <a href="https://blog.codersee.com/reactive-mongodb-rest-api-crud-with-spring-boot-and-kotlin/">Reactive MongoDB REST API CRUD with Spring Boot and Kotlin</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/reactive-mongodb-rest-api-crud-with-spring-boot-and-kotlin/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-04-26 10:44:37 by W3 Total Cache
-->