<?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>Micronaut Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/category/micronaut/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.codersee.com/category/micronaut/</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>Micronaut Archives - Codersee blog- Kotlin on the backend</title>
	<link>https://blog.codersee.com/category/micronaut/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Testing Micronaut Application in Kotlin</title>
		<link>https://blog.codersee.com/testing-micronaut-appplication-kotlin/</link>
					<comments>https://blog.codersee.com/testing-micronaut-appplication-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Wed, 10 Jan 2024 08:24:44 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[JUnit 5]]></category>
		<category><![CDATA[MockK]]></category>
		<category><![CDATA[REST Assured]]></category>
		<category><![CDATA[Testing]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9508620</guid>

					<description><![CDATA[<p>In this, practical tutorial, I will show you how to test a REST API created with Micronaut, Kotlin and MongoDB.</p>
<p>The post <a href="https://blog.codersee.com/testing-micronaut-appplication-kotlin/">Testing Micronaut Application in Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Hello and welcome to my next article, in which I will show you <strong>how to test a Micronaut application in Kotlin</strong>. </p>



<p>To be even more specific- we will work with an application that exposes a <strong>REST API</strong> and connects to <strong>MongoDB</strong>. If you would like to learn how to do that step-by-step, then you can check out <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/" target="_blank" rel="noreferrer noopener">my other article</a>. (But don&#8217;t worry, we will see its code snippets in this tutorial, too). </p>



<p>For testing, we&#8217;re going to use <strong>JUnit 5</strong>, <strong>MockK</strong>, <strong>REST Assured</strong>, as well as the <strong>@MicronautTest</strong> and <strong>Micronaut Test Resources</strong>.</p>



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



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



<div style="text-align: center; width: 90%; margin-left: 5%;">
<a href="https://blog.codersee.com/testing-micronaut-appplication-kotlin/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FH-LbDsi4qKg%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p></div>



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



<h2 class="wp-block-heading" id="h-micronaut-project-overview">Micronaut Project Overview</h2>



<p>Let&#8217;s start everything by <strong>checking the project we will test today</strong>. Of course, we will focus on the most important parts, and for the full source code, I invite you to <a href="https://github.com/codersee-blog/kotlin-micronaut-testing" target="_blank" rel="noreferrer noopener">this GitHub repository</a>.</p>



<h3 class="wp-block-heading" id="h-application-properties">Application Properties</h3>



<p>In our project, we introduce 3 application properties files: </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="">// application.properties
micronaut.application.name=micronaut-testing

// application-mongo.properties
mongodb.uri=mongodb://localhost:27017/example

// application-test.properties
mongodb.package-names=com.codersee.model</pre>



<p>The application name is the default property inserted when generating a new project. </p>



<p>Nevertheless, the two remaining are created on purpose. With their names- <code>*mongo</code>, <code>*test</code>&#8211; we instruct Micronaut that it should consider them only when <code>mongo</code> and <code>test</code> profiles are active. </p>



<p>But why this way? Well, to avoid two issues. </p>



<h3 class="wp-block-heading" id="h-mongodb-connection-issues-with-micronaut-test-resources">MongoDB Connection Issues with Micronaut Test Resources</h3>



<p>So, let&#8217;s start everything by explaining <strong>why we don&#8217;t put the Mongo URI inside the main properties</strong>.</p>



<p>Well, when we use the Micronaut Test Resources, MongoDB support will start a MongoDB container and provide the value of the <code>mongodb.uri</code> property. </p>



<p>Nevertheless, <strong>this won&#8217;t happen</strong> when we explicitly set the URI in the application properties. And that&#8217;s why we want to make this setting available <strong>only when we set the environment to <code>mongo</code>.</strong></p>



<h3 class="wp-block-heading" id="h-codeccachekey-issue">CodecCacheKey Issue</h3>



<p>Additionally, if we don&#8217;t specify explicitly the package name, where our model classes live, we&#8217;re going to end up with the following issue when running our Micronaut tests: </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="">[io-executor-thread-1] ERROR i.m.http.server.RouteExecutor - Unexpected error occurred: Can't find a codec for CodecCacheKey{clazz=class com.codersee.model.AppUser, types=null}.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class com.codersee.model.AppUser, types=null}.
  at org.bson.internal.ProvidersCodecRegistry.lambda$get$0(ProvidersCodecRegistry.java:87)
  at java.base/java.util.Optional.orElseGet(Optional.java:364)
  at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:80)
  at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:50)
  at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:188)
...</pre>



<p>To me, looks like a bug. But thankfully, we can easily avoid that by putting that in the <code>application-test.properties</code> (which is used in tests by default).</p>



<h3 class="wp-block-heading" id="h-models">Models</h3>



<p>Following, we introduce a bunch of models:</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="">// Address.kt: 

import io.micronaut.serde.annotation.Serdeable

@Serdeable.Serializable
@Serdeable.Deserializable
data class Address(
  val street: String,
  val city: String,
  val code: Int
)

// AppUser.kt:

import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity

@MappedEntity
data class AppUser(
  @field:Id
  @field:GeneratedValue
  val id: String? = null,
  val firstName: String,
  val lastName: String,
  val email: String,
  val address: Address
)

// AppUserRequest.kt: 

import io.micronaut.serde.annotation.Serdeable

@Serdeable.Deserializable
data class AppUserRequest(
  val firstName: String,
  val lastName: String,
  val email: String,
  val street: String,
  val city: String,
  val code: Int
)

// SearchRequest.kt:

import io.micronaut.serde.annotation.Serdeable

@Serdeable.Deserializable
data class SearchRequest(val name: String)</pre>



<p>Those classes are either used to persist/retrieve data from MongoDB or when deserializing JSON payloads into the objects. </p>



<h3 class="wp-block-heading" id="h-repository">Repository</h3>



<p>Following, we have a repository layer responsible for communicating with our storage:</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="">// AppUserRepository.kt: 

import com.codersee.model.AppUser
import io.micronaut.data.mongodb.annotation.MongoFindQuery
import io.micronaut.data.mongodb.annotation.MongoRepository
import io.micronaut.data.repository.CrudRepository

@MongoRepository
interface AppUserRepository : CrudRepository&lt;AppUser, String> {

  fun findByFirstNameEquals(firstName: String): List&lt;AppUser>

  @MongoFindQuery("{ firstName: { \$regex: :name}}")
  fun findByFirstNameLike(name: String): List&lt;AppUser>
}</pre>



<h3 class="wp-block-heading" id="h-service">Service</h3>



<p>Nextly, the service layer, where we inject the repository and encapsulate its methods: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// AppUserService.kt:

import com.codersee.model.Address
import com.codersee.model.AppUser
import com.codersee.model.AppUserRequest
import com.codersee.repository.AppUserRepository
import io.micronaut.http.HttpStatus
import io.micronaut.http.exceptions.HttpStatusException
import jakarta.inject.Singleton

@Singleton
class AppUserService(
  private val appUserRepository: AppUserRepository
) {

  fun create(userRequest: AppUserRequest): AppUser =
    appUserRepository.save(
      userRequest.toAppUserEntity()
    )

  fun findAll(): List&lt;AppUser> =
    appUserRepository
      .findAll()
      .toList()

  fun findById(id: String): AppUser =
    appUserRepository.findById(id)
      .orElseThrow { HttpStatusException(HttpStatus.NOT_FOUND, "User with id: $id was not found.") }

  fun update(id: String, updateRequest: AppUserRequest): AppUser {
    val foundUser = findById(id)

    val updatedEntity =
      updateRequest
        .toAppUserEntity()
        .copy(id = foundUser.id)

    return appUserRepository.update(updatedEntity)
  }

  fun deleteById(id: String) {
    val foundUser = findById(id)

    appUserRepository.delete(foundUser)
  }

  fun findByNameLike(name: String): List&lt;AppUser> =
    appUserRepository
      .findByFirstNameLike(name)

  private fun AppUserRequest.toAppUserEntity(): AppUser {
    val address = Address(
      street = this.street,
      city = this.city,
      code = this.code
    )

    return AppUser(
      id = null,
      firstName = this.firstName,
      lastName = this.lastName,
      email = this.email,
      address = address
    )
  }
}</pre>



<h3 class="wp-block-heading" id="h-controller">Controller </h3>



<p>And lastly, the place where we expose our REST endpoints: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">// AppUserController.kt: 

import com.codersee.model.AppUserRequest
import com.codersee.service.AppUserService
import com.codersee.model.SearchRequest
import com.codersee.model.AppUser
import io.micronaut.http.HttpStatus
import io.micronaut.http.annotation.*
import io.micronaut.scheduling.TaskExecutors
import io.micronaut.scheduling.annotation.ExecuteOn

@Controller("/users")
@ExecuteOn(TaskExecutors.IO)
class AppUserController(
  private val appUserService: AppUserService
) {

  @Get
  fun findAllUsers(): List&lt;AppUser> =
    appUserService.findAll()

  @Get("/{id}")
  fun findById(@PathVariable id: String): AppUser =
    appUserService.findById(id)

  @Post
  @Status(HttpStatus.CREATED)
  fun createUser(@Body request: AppUserRequest): AppUser =
    appUserService.create(request)

  @Post("/search")
  fun searchUsers(@Body searchRequest: SearchRequest): List&lt;AppUser> =
    appUserService.findByNameLike(
      name = searchRequest.name
    )

  @Put("/{id}")
  fun updateById(
    @PathVariable id: String,
    @Body request: AppUserRequest
  ): AppUser =
    appUserService.update(id, request)

  @Delete("/{id}")
  @Status(HttpStatus.NO_CONTENT)
  fun deleteById(@PathVariable id: String) =
    appUserService.deleteById(id)
}</pre>



<h2 class="wp-block-heading" id="h-simple-kotlin-testing-in-micronaut">Simple Kotlin Testing in Micronaut</h2>



<p>Excellent. At this point, we know what exactly we are going to test. </p>



<p>And as the first approach we&#8217;re going to see will be a plain, old unit test. </p>



<p>Why? </p>



<p>Because at the end of the day, <strong>that&#8217;s what we will be dealing with most of the time</strong>. </p>



<p>So with that said, let&#8217;s take a look at the example test: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">  import com.codersee.repository.AppUserRepository
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class AppUserServiceTest {

  private val appUserRepository = mockk&lt;AppUserRepository>()

  private val appUserService = AppUserService(appUserRepository)

  @Test
  fun `should return empty user list`() {
    every {
      appUserRepository.findAll()
    } returns emptyList()

    val result = appUserService.findAll()

    assertTrue(result.isEmpty())

    verify(exactly = 1) { appUserRepository.findAll() }
  }
}</pre>



<p>As we can see above, we simply mock (with MockK) the <em>AppUserRepository </em>and inject it into the <em>AppUserService </em>instance.</p>



<p>Then, we specify that all invocations of the <code>findAll</code> method must return the empty list (with <code>every</code>). </p>



<p>Lastly, we assert that the <code>findAll</code> method from our service returns an empty list (<code>assertTrue</code>) and that the <code>findAll</code> method from the repository was invoked only once (<code>verify</code>). </p>



<p>Nevertheless, we&#8217;re not going to spend too much here right now as this tutorial focuses on Micronaut testing in Kotlin. If you are interested in learning more about these tools, then let me know in the comments section 🙂 </p>



<h2 class="wp-block-heading" id="h-integration-testing-with-micronauttest">Integration Testing with @MicronautTest</h2>



<p>With all of that done, let&#8217;s see how the Micronaut framework helps us with testing and a few interesting cases that will help us in real life. </p>



<h3 class="wp-block-heading" id="h-what-is-a-micronauttest">What is a @MicronautTest? </h3>



<p>Let&#8217;s start everything by explaining what exactly is the <strong>@MicronautTest</strong>.</p>



<p>In simple words, it&#8217;s an annotation that we can put on the test class to mark it a Micronaut test (no sh&#8230; Sherlock 🙂 :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.micronaut.test.extensions.junit5.annotation.MicronautTest

@MicronautTest
class SomeTest {
  // tests
}</pre>



<p>In practice, it means that <strong>when we run a test with that annotation, it will run a real application</strong>. It will use internal Micronaut features with no mocking. So at this point, we can clearly see that this will be the right solution for <strong>integration testing.</strong></p>



<p>Moreover, this annotation can be used not only with <strong>JUnit 5</strong>, but also with <strong>Spock</strong>, <strong>Kotest</strong>, and <strong>Kotest 5</strong>. </p>



<h3 class="wp-block-heading" id="h-micronauttest-configuration-options">@MicronautTest Configuration Options </h3>



<p>The @MicronautTest allows us to configure a few properties, like the environments we want to run our test with, the packages to scan, or whether the automatic transaction wrapping should be enabled, or not:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.micronaut.test.extensions.junit5.annotation.MicronautTest

@MicronautTest(
  environments = ["env-1, env-2"],
  packages = ["com.codersee.somepackage"],
  transactional = false
)
class SomeTest {
  // tests
}</pre>



<p>For the full list of options, I highly encourage you to check out the docs (we can do that for instance in IntelliJ IDEA by clicking on the annotation with the left mouse button when we keep the left ctrl pressed). </p>



<h3 class="wp-block-heading" id="h-what-are-micronaut-test-resources">What Are Micronaut Test Resources? </h3>



<p>At the beginning of this tutorial, I mentioned the Micronaut Test Resources and I would like to make sure that we are on the same page with them. </p>



<p>So basically, Micronaute Test Resources <strong>integrates seamlessly with Testcontainers to provide throwaway containers for testing</strong>. But, we need to remember that Testcontainers <strong>require Docker-API compatible container runtime</strong> (in simple words- either Docker installed locally, or the Testcontainers Cloud). </p>



<p>In our case, we would like to do integration tests that require MongoDB. We could make use of the real database (which sometimes may be the case for you), provide some H2 database (not the best approach), or integrate Testcontainers. Thankfully, we don&#8217;t need to do that and the only thing we need is the appropriate import in our project: </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="">plugins {
    id("io.micronaut.test-resources") version "4.2.1"
}</pre>



<p>As a reminder, I want to emphasize that <strong>test resources will be used only when the <code>datasources.default.url</code> is missing.</strong> (and that&#8217;s why we removed the URI for MongoDB from tests). <strong> </strong> </p>



<h2 class="wp-block-heading" id="h-integration-tests-with-rest-assured">Integration Tests With REST Assured</h2>



<p>With all of that said, let&#8217;s combine everything together and test our Kotlin Micronaut application with JUnit 5 and REST Assured. </p>



<p>To do so, we must remember to add the necessary import: </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="">testImplementation("io.micronaut.test:micronaut-test-rest-assured")</pre>



<h3 class="wp-block-heading" id="h-verify-status-codes-and-headers">Verify Status Codes and Headers</h3>



<p>Let&#8217;s start with a simple request to the <code>GET /users</code> 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="">@MicronautTest
class AppUserControllerTestWithoutMocking {

  @Test
  fun `should return 200 OK on GET users`(spec: RequestSpecification) {
    spec
      .`when`()
      .get("/users")
      .then()
      .statusCode(200)
      .header("Content-Type", "application/json")
  }

}</pre>



<p>As I mentioned previously- by default, with @MicronautTest the real application is started. Moreover, nothing here is mocked, so the Mongo test container delivered by the Micronaut Test Resources is used.</p>



<p>Thanks to the dedicated module we added, we can inject the <code>RequestSpecification</code> into our tests and easily validate our endpoint. </p>



<p>In this case, we <strong>perform a GET request</strong> and verify that the <strong>response status code is 200 OK</strong> and the response <strong><code>Content-Type</code> header value is <code>application/json</code></strong>. </p>



<h3 class="wp-block-heading" id="h-verify-404-not-found">Verify 404 Not Found</h3>



<p>Similarly, we can verify that no entry is present in the database: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Test
fun `should return 404 Not Found on GET user by ID`(spec: RequestSpecification) {
  spec
    .`when`()
    .get("/users/123123123123123123121231")
    .then()
    .statusCode(404)
}</pre>



<p>This test will be also good proof that entries inserted to MongoDB in other tests do not affect other tests. </p>



<h3 class="wp-block-heading" id="h-extract-and-json-array-in-rest-assured">Extract and JSON Array in REST Assured</h3>



<p>Nextly, let&#8217;s take a look at the more complicated 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="">@Test
fun `should create a user`(spec: RequestSpecification) {
  val request = AppUserRequest(
    firstName = "Piotr",
    lastName = "Wolak",
    email = "contact@codersee.com",
    street = "Street",
    city = "City",
    code = 123,
  )

  spec
    .`when`()
    .contentType(ContentType.JSON)
    .body(request)
    .post("/users")
    .then()
    .statusCode(201)

  val list = spec
    .`when`()
    .get("/users")
    .then()
    .statusCode(200)
    .body("size()", `is`(1))
    .extract()
    .`as`(object : TypeRef&lt;List&lt;AppUser>>() {})

  assertEquals(1, list.size)

  val createdUser = list.first()

  assertEquals(request.firstName, createdUser.firstName)
  assertEquals(request.street, createdUser.address.street)
}</pre>



<p>This time, we do a bunch of more interesting things. </p>



<p>Firstly, we make the <strong>POST request </strong>with a request body and <strong>verify that 201 Creates is returned</strong>. </p>



<p>After that, we call the <code>GET /users</code> endpoint to get a list of users. When we get the response, we <strong>verify that the JSON array size is equal to 1</strong> (with the <code>.body("size()", is(1))</code> call). Lastly, we <strong>make use of the extract().`as</strong>` to convert the payload into the List of AppUser instances. Thanks to that we can easily perform assertions with JUnit 5 functions. </p>



<h3 class="wp-block-heading" id="h-add-kotlin-utils-for-rest-assured">Add Kotlin Utils For REST Assured</h3>



<p>As you probably know, <code>when</code> and <code>as</code> are keywords in Kotlin and that&#8217;s why <strong>we had to use backticks (&#8220;)</strong>.</p>



<p>And to make our lives easier, let&#8217;s add the <code>util</code> package in <code>test</code> and create the <code>TestUtil.kt</code>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.restassured.common.mapper.TypeRef
import io.restassured.response.ValidatableResponse
import io.restassured.specification.RequestSpecification


fun RequestSpecification.whenever(): RequestSpecification {
  return this.`when`()
}

fun &lt;T> ValidatableResponse.extractAs(clazz: Class&lt;T>) =
  this.extract()
    .`as`(clazz)

fun &lt;T> ValidatableResponse.extractAs(typeRef: TypeRef&lt;T>) =
  this.extract()
    .`as`(typeRef)</pre>



<p>As a result, from now on we can simply use the <em>whenever() </em>instead of <em>`when`()</em> and <em>extractAs() </em>instead of <em>extract().`</em>as`(). </p>



<h2 class="wp-block-heading" id="h-reference-the-server-context">Reference The Server / Context </h2>



<p>As the next step, let&#8217;s take a look at how to <strong>reference the server</strong> or <strong>current application context</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="">@MicronautTest
class InjectingExample{

  @Inject
  private lateinit var context: ApplicationContext

  @Inject
  private lateinit var server: EmbeddedServer

  // tests
}</pre>



<p>Sometimes it can be useful, and as we can see, we can simply inject them using the <strong>@Inject</strong> annotation. </p>



<h2 class="wp-block-heading" id="h-conditionally-run-tests">Conditionally Run Tests</h2>



<p>After that, let&#8217;s see <strong>how we can skip tests execution.</strong> </p>



<p>Why would we need that? </p>



<p>Well, integration tests sometimes can take a lot of time, and the bigger our project becomes, the bigger the chance they become annoying. And because of that, generally, it&#8217;s a good approach to somehow separate them from the faster unit tests. </p>



<p> When testing in Micronaut, we can easily achieve that using the <strong><em>@Requires</em></strong> annotation:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import io.micronaut.context.annotation.Requires
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

@MicronautTest
@Requires(env = ["integration-test"])
class SomeIntegrationTest {

  @Test
  fun `should perform integration test`() {
    assertTrue(false)
  }

}</pre>



<p>The <strong><em>@MicronautTest</em></strong> annotation <strong>turns tests into beans</strong>. And that&#8217;s why we can use the <strong>@Requires</strong>, just like we would do with a &#8220;standard&#8221; bean.</p>



<p>As a result, tests inside the <code>SomeIntegrationTest</code> will run only, when the <code>integration-test</code> the environment is active. (And we can set that for example by setting the environment variable- <code>MICRONAUT_ENVIRONMENTS=integration-test</code>). </p>



<h2 class="wp-block-heading" id="h-testing-micronaut-with-mockk-mocks">Testing Micronaut With MockK Mocks</h2>



<p>As the last thing in our tutorial about testing Micronaut with Kotlin, let&#8217;s take a look at <strong>how we can mock things using the MockK</strong>.</p>



<p>Could we use Mockito? Yes. However, I find MockK better for Kotlin (DSLs &lt;3 ).</p>



<p>In order to mock a bean in Micronaut, the only thing we need to do is <strong>annotate the method/inner class with the @MockBean annotation</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="">@MicronautTest
class AppUserControllerTestWithMocking {

  private val repo: AppUserRepository = mockk&lt;AppUserRepository>()

  @MockBean(AppUserRepository::class)
  fun appUserRepository(): AppUserRepository = repo
}</pre>



<p>As we can see, we introduce the <code>AppUserRepository</code> mock as the <code>repo</code> property, so that later, we will be able to refer to it easily in our test cases. Additionally, we add the function annotated with <code>@MockBean</code>, thus informing Micronaut that the <code>AppUserRepository</code> is the type we want to replace with our mock. </p>



<p>Moreover, thanks to this approach <strong>this mock will be limited only to tests defined in this class</strong>. So we can &#8220;rest assured&#8221; (hue hue) it won&#8217;t interfere with other classes. </p>



<p>As a result, our tests will look, as follows: </p>



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

  private val repo: AppUserRepository = mockk&lt;AppUserRepository>()

  @Test
  fun `should return 200 OK on GET users`(spec: RequestSpecification) {
    every {
      repo.findAll()
    } returns emptyList()

    spec
      .whenever()
      .get("/users")
      .then()
      .statusCode(200)
      .header("Content-Type", "application/json")
  }

  @Test
  fun `should return user by ID`(spec: RequestSpecification) {
    val foundUser = AppUser(
      id = "123",
      firstName = "Piotr",
      lastName = "Wolak",
      email = "contact@codersee.com",
      address = Address(
        street = "street",
        city = "city",
        code = 123
      )
    )

    every {
      repo.findById("123")
    } returns Optional.of(foundUser)

    spec
      .whenever()
      .get("/users/123")
      .then()
      .statusCode(200)
      .body("id", equalTo("123"))
      .body("firstName", equalTo("Piotr"))
      .body("address.street", equalTo("street"))
      .body("address.code", equalTo(123))
  }

  @Test
  fun `should return user by ID and verify with extract`(spec: RequestSpecification) {
    val foundUser = AppUser(
      id = "123",
      firstName = "Piotr",
      lastName = "Wolak",
      email = "contact@codersee.com",
      address = Address(
        street = "street",
        city = "city",
        code = 123
      )
    )

    every {
      repo.findById("123")
    } returns Optional.of(foundUser)

    val extracted = spec
      .whenever()
      .get("/users/123")
      .then()
      .statusCode(200)
      .extractAs(AppUser::class.java)

    assertEquals(foundUser, extracted)
  }

  @Test
  fun `should create a user`(spec: RequestSpecification) {
    val request = AppUserRequest(
      firstName = "Piotr",
      lastName = "Wolak",
      email = "contact@codersee.com",
      street = "Street",
      city = "City",
      code = 123,
    )

    val createdUser = AppUser(
      id = "123",
      firstName = "Piotr",
      lastName = "Wolak",
      email = "contact@codersee.com",
      address = Address(
        street = "street",
        city = "city",
        code = 123
      )
    )

    every {
      repo.save(any())
    } returns createdUser

    val extracted = spec
      .whenever()
      .contentType(ContentType.JSON)
      .body(request)
      .post("/users")
      .then()
      .statusCode(201)
      .extractAs(AppUser::class.java)

    assertEquals(createdUser, extracted)
  }

  @MockBean(AppUserRepository::class)
  fun appUserRepository(): AppUserRepository = repo
}</pre>



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



<p>And that&#8217;s all for this tutorial on <strong>how to perform testing in Micronaut with Kotlin.</strong></p>



<p>Together, we&#8217;ve discovered a bunch of interesting things that I hope will be useful in your projects. </p>



<p>If you would like to see the codebase for this tutorial, then check out <a href="https://github.com/codersee-blog/kotlin-micronaut-testing" target="_blank" rel="noreferrer noopener">this GitHub repository</a>. Or, if you are interested in learning more about Micronaut, then check out <a href="https://blog.codersee.com/category/micronaut/">my other posts</a>. </p>



<p>Let me know your thoughts in the comments section below! 🙂 </p>
<p>The post <a href="https://blog.codersee.com/testing-micronaut-appplication-kotlin/">Testing Micronaut Application in 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/testing-micronaut-appplication-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Fix &#8220;Can&#8217;t find a codec for CodecCacheKey&#8221; in Micronaut</title>
		<link>https://blog.codersee.com/fix-cant-find-codec-for-codeccachekey-micronaut/</link>
					<comments>https://blog.codersee.com/fix-cant-find-codec-for-codeccachekey-micronaut/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 09 Jan 2024 16:15:00 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Testing]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=9508615</guid>

					<description><![CDATA[<p>In this short article, I will show you how to solve the "Unexpected error occurred: Can't find a codec for CodecCacheKey" in Micronaut. </p>
<p>The post <a href="https://blog.codersee.com/fix-cant-find-codec-for-codeccachekey-micronaut/">Fix &#8220;Can&#8217;t find a codec for CodecCacheKey&#8221; in Micronaut</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>If you have ever struggled with the <strong><em>&#8220;Unexpected error occurred: Can&#8217;t find a codec for CodecCacheKey&#8221;</em></strong> when testing in <strong>Micronaut</strong>, then you came to the right place 🙂 </p>



<p>In this short article, I will show you how to fix this issue based on the example project. </p>



<h2 class="wp-block-heading" id="h-trigger-the-codeccachekey-in-micronaut-test">Trigger The CodecCacheKey in Micronaut Test</h2>



<p>Before I show you how to fix this issue, let me quickly introduce you to how I trigger the CodecCacheKey issue when writing tests in Micronaut. (If you are interested in the whole project, check <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/">this article about Micronaut with MongoDB</a>).</p>



<p>So firstly, I created a simple <code>AppUser</code> 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="">import io.micronaut.data.annotation.GeneratedValue
import io.micronaut.data.annotation.Id
import io.micronaut.data.annotation.MappedEntity

@MappedEntity
data class AppUser(
  @field:Id
  @field:GeneratedValue
  val id: String? = null,
  val firstName: String,
  val lastName: String,
  val email: String,
  val address: Address
)</pre>



<p>Nothing spectacular. Just a simple class with a few annotations necessary to work with MongoDB and generate identifiers automatically. </p>



<p>As the next step, I added the repository, which looked, as follows: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@MongoRepository
interface AppUserRepository : CrudRepository&lt;AppUser, String> {
 
  // few methods 
}</pre>



<p>And all of that together with other classes that are not important in this tutorial, created a structure, like this: </p>



<figure class="wp-block-image aligncenter size-full is-resized"><img fetchpriority="high" decoding="async" width="323" height="408" src="http://blog.codersee.com/wp-content/uploads/2024/01/micronaut_mongodb_codeccachekey_project_structure.png" alt="Image presents the package structure of the backend Kotlin project created with Micronaut that was used to trigger the &quot;Can't find a codec for CodecCacheKey&quot; issue" class="wp-image-9508616" style="width:323px;height:auto" srcset="https://blog.codersee.com/wp-content/uploads/2024/01/micronaut_mongodb_codeccachekey_project_structure.png 323w, https://blog.codersee.com/wp-content/uploads/2024/01/micronaut_mongodb_codeccachekey_project_structure-238x300.png 238w" sizes="(max-width: 323px) 100vw, 323px" /></figure>



<p>So, lastly, I added a simple test with <em>@MicronautTest</em>, <em>JUnit 5</em>, and <em>REST Assured</em> to test whether my REST endpoint returns users from the Mongo database:</p>



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

  @Test
  fun `should return 200 OK on GET users`(spec: RequestSpecification) {
    spec
      .`when`()
      .get("/users")
      .then()
      .statusCode(200)
      .header("Content-Type", "application/json")
  }
}</pre>



<p> As a result, instead of the beautiful, green icon indicating that everything is fine, <strong>I got the following:</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="">[io-executor-thread-1] ERROR i.m.http.server.RouteExecutor - Unexpected error occurred: Can't find a codec for CodecCacheKey{clazz=class com.codersee.model.AppUser, types=null}.
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for CodecCacheKey{clazz=class com.codersee.model.AppUser, types=null}.
	at org.bson.internal.ProvidersCodecRegistry.lambda$get$0(ProvidersCodecRegistry.java:87)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:80)
	at org.bson.internal.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:50)
	at com.mongodb.internal.operation.Operations.createFindOperation(Operations.java:188)
...</pre>



<p>Moreover, when I ran the application manually and tested using a real MongoDB instance, <strong>everything worked fine</strong>. </p>



<h2 class="wp-block-heading" id="h-how-to-fix-codeccachekey-issue">How To Fix CodecCacheKey Issue?</h2>



<p>In order to fix the issue, I created a new file- called <code>application-test.properties</code> in the <code>resources</code> directory and put the following:</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="">mongodb.package-names=com.codersee.model</pre>



<p>But what exactly does it do? </p>



<p>Well, firstly, the application-test name of the file instructs the Micronaut framework to use this property file only when running tests. </p>



<p>Secondly, with the <code>mongodb.package-names</code>, I set package names to allow for POJOs. And that&#8217;s why I put the <code>com.codersee.model</code> only. </p>



<p>And voila, that&#8217;s all 🙂 </p>



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



<p>And basically, that&#8217;s all for this short tutorial on how to fix the issue with CodecCacheKey in Micronaut. </p>



<p>Thank you for being here, and if you would like to learn more about the framework, then check out other posts from the <a href="https://blog.codersee.com/category/micronaut/">Micronaut category</a>. </p>



<p>If you would like to see the codebase for this tutorial, please refer to <a href="https://github.com/codersee-blog/micronaut-kotlin-mongodb-revisited" target="_blank" rel="noreferrer noopener">my GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/fix-cant-find-codec-for-codeccachekey-micronaut/">Fix &#8220;Can&#8217;t find a codec for CodecCacheKey&#8221; in Micronaut</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/fix-cant-find-codec-for-codeccachekey-micronaut/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Micronaut With Kotlin and MongoDB Video Tutorial</title>
		<link>https://blog.codersee.com/micronaut-with-kotlin-and-mongodb-video-tutorial/</link>
					<comments>https://blog.codersee.com/micronaut-with-kotlin-and-mongodb-video-tutorial/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Fri, 25 Nov 2022 07:47:33 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[Core Kotlin]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504010</guid>

					<description><![CDATA[<p>Finally, I've released a new Micronaut with Kotlin and MongoDB video tutorial for free, so it's time to learn something new. </p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-kotlin-and-mongodb-video-tutorial/">Micronaut With Kotlin and MongoDB Video Tutorial</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Hello friends 😀 This blog post is a bit different than the other how-to articles I publish because I simply wanted to let you know that I published a new<strong> Micronaut with Kotlin and MongoDB video tutorial.</strong></p>
<p>If you would like to learn:</p>
<ul>
<li><strong>what exactly the Micronaut Framework </strong>is and what problems can you solve with it?</li>
<li><strong>how to create REST API</strong> using Micronaut and Kotlin programming language?</li>
<li>how to set up a <strong>MongoDB instance with Docker</strong> and view the data with <strong>MongoDB Compass</strong>?</li>
<li>and furthermore, how to set up all of the above step-by-step?</li>
</ul>
<p>Then the Micronaut with Kotlin and MongoDB video tutorial will help you with all of the above. <strong>12 free videos</strong> with around <strong>56 minutes</strong> of the condensed knowledge are a great way to start your journey with the Micronaut framework.</p>
<p>&nbsp;</p>
<p><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>As always, you can find it entirely for free on this blog, by simply clicking the <em>Video Tutorials</em> tab in the header, or by navigating to it with <a href="https://codersee.com/courses/rest-api-with-micronaut-kotlin-and-mongodb-course/">this, direct link</a>.</p>
<p>Alternatively, you can find it on the YouTube platform as a playlist <a href="https://youtube.com/playlist?list=PLvN8k8yxjoetVCPwyc9KyS4zZMjD9X3do" target="_blank" rel="noopener">right here</a>, but right there I cannot guarantee that you won&#8217;t see the ads, or that everything will be displayed in the correct order.</p>
<p>&nbsp;</p>
<p><img decoding="async" class="aligncenter wp-image-5504011" src="http://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist-1024x1024.png" alt="The image is a Drake meme joke showing that it's better to use the Codersee internal courses page for Micronaut with Kotlin and MongoDB video tutorial." width="600" height="600" srcset="https://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist-1024x1024.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist-300x300.png 300w, https://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist-150x150.png 150w, https://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist-768x768.png 768w, https://blog.codersee.com/wp-content/uploads/2022/11/drake_meme_prefer_codersee_over_playlist.png 1200w" sizes="(max-width: 600px) 100vw, 600px" /></p>
<p>&nbsp;</p>
<h2>One more thing&#8230;</h2>
<p>Finally, I just wanted to thank you for being a part of the Codersee community and ask you about one thing: <strong>I would be forever thankful if you share your feedback with me. </strong>Of course, regardless of whether you enjoy the course or you think it&#8217;s just a piece of s**t. I would love to hear your thoughts and how could I improve my content to better suit your needs. You can do it wherever you want: by using the contact form, using a comments section below, or even on YouTube.</p>
<p>Thank you for your time and have a great week!</p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-kotlin-and-mongodb-video-tutorial/">Micronaut With Kotlin and MongoDB Video Tutorial</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/micronaut-with-kotlin-and-mongodb-video-tutorial/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Reactive REST API with Micronaut and MongoDB</title>
		<link>https://blog.codersee.com/reactive-rest-api-with-micronaut-and-mongodb/</link>
					<comments>https://blog.codersee.com/reactive-rest-api-with-micronaut-and-mongodb/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 15 Nov 2022 06:00:17 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503861</guid>

					<description><![CDATA[<p>In this continuation, I will show you how to create a reactive REST API with Micronaut and MongoDB and data repositories.</p>
<p>The post <a href="https://blog.codersee.com/reactive-rest-api-with-micronaut-and-mongodb/">Reactive REST API with Micronaut and MongoDB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>1. Introduction</h2>
<p>Hello 😀 In this article, I will show you how to implement a <strong>Reactive REST API with Micronaut and MongoDB (+ Project Reactor)</strong>.</p>
<p>Nevertheless, if you are not interested in a reactive approach, and you would like to create a simple REST API, then check out my <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/" target="_blank" rel="noopener">previous article related to Micronaut and Mongo</a>.</p>
<p>Finally, just wanted to let you know that after this tutorial, you will know precisely:</p>
<ul>
<li>how to spin up a <strong>MongoDB instance with Docker</strong>,</li>
<li><strong>create a new Micronaut project</strong> with all dependencies using the Launch page,</li>
<li>how to expose <strong>REST endpoints</strong>,</li>
<li>and how to <strong>validate user input</strong>.</li>
</ul>
<h2>2. MongoDB Instance Setup</h2>
<p>But before we create our Micronaut project, <strong>we need a MongoDB instance up and running on our local machine</strong>.</p>
<p>If you don&#8217;t have one, you can either install it with the <a href="https://www.mongodb.com/docs/manual/installation/" target="_blank" rel="noopener">official documentation</a> or simply run the following Docker commands:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker pull mongo</pre>
<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>
<p>Then, start a new container named <em>mongodb</em> in a detached mode:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker run -d -p 27017:27017 --name mongodb mongo</pre>
<p>And finally, to validate use the <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</code> command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">CONTAINER ID IMAGE COMMAND                CREATED       STATUS       PORTS                    NAMES
ce86244c3fe1 mongo "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:27017-&gt;27017/tcp mongodb</pre>
<h2>3. Generate Reactive Micronaut with MongoDB Project</h2>
<p>As the next step, let&#8217;s navigate to the <a href="https://micronaut.io/launch/" target="_blank" rel="noopener">Micronaut Launch</a>. It is a web application, which allows us to generate new projects from scratch with ease. Of course, we have other possibilities, like Micronaut CLI, or cURL, but for simplicity, let&#8217;s go with the web approach:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5503866 size-full" src="http://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page.png" alt="The image is a screenshot from the Micronaut Launch page showing all configuration required to set up a new reactive REST API with Micronaut and MongoDB project from scratch." width="1616" height="682" srcset="https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page.png 1616w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page-300x127.png 300w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page-1024x432.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page-768x324.png 768w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-launch-page-1536x648.png 1536w" sizes="auto, (max-width: 1616px) 100vw, 1616px" /></p>
<p>As we can see, we will be using <strong>Micronaut 3.7.1</strong> with <strong>Kotlin</strong> and <strong>JUnit</strong>.</p>
<p>Additionally, to set up reactive Micronaut with MongoDB, we will need the following features:</p>
<ul>
<li><strong>reactor </strong>&#8211; adding reactive support using <em>Project Reactor,</em></li>
<li><strong>mongo-reactive</strong> &#8211; bringing support for the <em>MongoDB Reactive Streams Driver,</em></li>
<li><strong><span class="card-title">data-mongodb-reactive</span></strong> &#8211; adding reactive <em>data repositories</em> for MongoDB.</li>
</ul>
<p>With all of that being selected, let&#8217;s click the <strong>Generate Project</strong> button, download the ZIP file, and import it to our IDE.</p>
<h2>4. application.yaml</h2>
<p>As the next step after import, let&#8217;s make sure that our <code class="EnlighterJSRAW" data-enlighter-language="raw">application.yaml</code> is configured properly:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="yaml">micronaut:
  application:
    name: mongodbasync
netty:
  default:
    allocator:
      max-order: 3
mongodb.uri: mongodb://${MONGO_HOST:localhost}:${MONGO_PORT:27017}/someDb</pre>
<p>Please keep in mind to set the <code class="EnlighterJSRAW" data-enlighter-language="raw">mongodb.uri</code> to match your case.</p>
<p>With the above configuration, Micronaut will try to connect to the instance using <strong>localhost:27017</strong> and use the <strong>someDb</strong> database. It&#8217;s worth mentioning here, that we don&#8217;t have to create the database manually- it will be created automatically if it does not exist.</p>
<h2>5. Create Models and DTOs</h2>
<p>Following, let&#8217;s add classes responsible for persisting and fetching data from MongoDB.</p>
<p>And as an introduction- in this project, we will expose functionality to manage <strong>articles</strong> and <strong>their authors</strong>.</p>
<h3>5.1. Add Article Class</h3>
<p>Firstly, let&#8217;s implement the <strong>Article</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@MappedEntity
data class Article(
  @field:Id
  @field:GeneratedValue
  val id: String? = null,
  val title: String,
  val category: ArticleCategory,
  val author: Author
)</pre>
<p>As we can clearly see, we must annotate this class with:</p>
<ul>
<li><strong><em>@MappedEntity</em></strong>&#8211; a generic annotation used to identify a persistent type. Without it, we would end up with an error: <code class="EnlighterJSRAW" data-enlighter-language="raw">Internal Server Error: Can't find a codec for class com.codersee.model.Article</code>, when adding a repository later.</li>
<li><strong><em>@Id</em></strong> &#8211; responsible for marking the identifier field. Without this one, on the other hand, the code won&#8217;t compile with the following message: <code class="EnlighterJSRAW" data-enlighter-language="raw">Unable to implement Repository method: ArticleRepository.delete(Object entity). Delete all not supported for entities with no ID</code></li>
<li><em><strong>@GeneratedValue</strong> </em>&#8211; annotating our property as a generated value.</li>
</ul>
<p>Additionally, we make use of the <strong>@field</strong> to specify how exactly annotation should be generated in the Java bytecode.</p>
<h3>5.2. Implement ArticleCategory</h3>
<p>Nextly, let&#8217;s add a simple enum called <strong>ArticleCategory</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">enum class ArticleCategory {
  JAVA, KOTLIN, JAVASCRIPT
}</pre>
<p>It&#8217;s a simple enum, which will identify categories of our articles.</p>
<h3>5.3. Create Author</h3>
<p>Following, let&#8217;s implement the <strong>Author</strong> data class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Serializable
@Deserializable
data class Author(
  val firstName: String,
  val lastName: String,
  val email: String
)</pre>
<p>This time, we have to mark the class with <strong>@Serializable</strong> and <strong>@Deserializable</strong>.</p>
<p>And although we don&#8217;t have to mark this class as an entity (it&#8217;s an inner JSON in the MongoDB document, not a separate one), we have to utilize these two annotations.</p>
<p>Without them, everything will fail with either:</p>
<blockquote><p>Caused by: io.micronaut.core.beans.exceptions.IntrospectionException: No serializable introspection present for type Author author. Consider adding Serdeable. Serializable annotate to type Author author. Alternatively if you are not in control of the project&#8217;s source code, you can use <span class="atMention" title="serdeimport">@serdeimport</span>(Author.class) to enable serialization of this type.</p></blockquote>
<p>or</p>
<blockquote><p>Caused by: io.micronaut.core.beans.exceptions.IntrospectionException: No deserializable introspection present for type: Author. Consider adding Serdeable.Deserializable annotate to type Author. Alternatively if you are not in control of the project&#8217;s source code, you can use <span class="atMention" title="serdeimport">@serdeimport</span>(Author.class) to enable deserialization of this type.</p></blockquote>
<h3>5.4. Implement Requests</h3>
<p>Finally, let&#8217;s implement two DTOs responsible for deserializing JSON payloads sent by the user: <strong>ArticleRequest</strong> and <strong>SearchRequest</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">data class ArticleRequest(
  val title: String,
  val category: ArticleCategory,
  val author: Author
)

data class SearchRequest(
  val title: String
)</pre>
<p>As we can see, these are just two, plain data classes with no annotations. And as mentioned above, their only responsibility will be to <strong>transfer the data deserialized from request bodies</strong>.</p>
<h2>6. Make Use Of Data Repositories</h2>
<p>With that being done, we can add the <strong>ArticleRepository</strong> to our reactive Micronaut with MongoDB project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@MongoRepository
interface ArticleRepository : ReactorCrudRepository&lt;Article, String&gt; {

  @MongoFindQuery("{ title: { \$regex: :title, '\$options' : 'i'}}")
  fun findByTitleLikeCaseInsensitive(title: String): Flux&lt;Article&gt;

}</pre>
<p>This time, we have to implement the desired repository and annotate the interface with <strong>@MongoRepository </strong>to make use of the data repositories in Micronaut.</p>
<p>Additionally, we&#8217;ve added a custom find query with <strong>@MongoFindQuery</strong> annotation. This function will be responsible for fetching articles by a given title.</p>
<p><strong>Note:</strong> when we check the <strong>GenericRepository</strong> interface, we will see that we have plenty of possibilities to pick from:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5503869 size-full" src="http://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-repositories-list.png" alt="The image is a screen of all interfaces extending the Micronaut GenericRepository." width="778" height="300" srcset="https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-repositories-list.png 778w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-repositories-list-300x116.png 300w, https://blog.codersee.com/wp-content/uploads/2022/11/reactive-rest-api-with-micronaut-and-mongodb-repositories-list-768x296.png 768w" sizes="auto, (max-width: 778px) 100vw, 778px" /></p>
<p>&nbsp;</p>
<p>In our case, we will go with the <strong>ReactorCrudRepository</strong>, which exposes basic CRUD operations with <em>Fluxes</em> and <em>Monos</em>.</p>
<h2>7. Add Service Layer</h2>
<p>Nextly, let&#8217;s implement the <strong>ArticleService</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class ArticleService(
  private val articleRepository: ArticleRepository
) {

  fun create(article: Article): Mono&lt;Article&gt; =
    articleRepository.save(article)

  fun findAll(): Flux&lt;Article&gt; =
    articleRepository.findAll()

  fun findById(id: String): Mono&lt;Article&gt; =
    articleRepository.findById(id)
      .switchIfEmpty(
        Mono.error(
          HttpStatusException(HttpStatus.NOT_FOUND, "Article with id: $id does not exists.")
        )
      )

  fun update(id: String, updated: Article): Mono&lt;Article&gt; =
    findById(id)
      .map { foundArticle -&gt; updated.copy(id = foundArticle.id) }
      .flatMap(articleRepository::update)

  fun deleteById(id: String): Mono&lt;Void&gt; =
    findById(id)
      .flatMap(articleRepository::delete)
      .flatMap { deletedCount -&gt;
        if (deletedCount &gt; 0L)
          Mono.empty()
        else
          Mono.error(
            HttpStatusException(HttpStatus.NOT_FOUND, "Article with id: $id was not deleted.")
          )
      }

  fun findByTitleLike(name: String): Flux&lt;Article&gt; =
    articleRepository.findByTitleLikeCaseInsensitive(name)
}</pre>
<p>As we can see, a lot is happening here, so let&#8217;s take a minute to understand this code snippet.</p>
<p>As the first thing, we mark the service with <strong>@Singleton</strong>, which means that the injector will instantiate this class <strong>only once</strong>. Then, we inject a previously created repository to make use of it.</p>
<h3>7.1. create(), findAll() and findByTitleLike()</h3>
<p>When it comes to these three functions there&#8217;s not too much to say about:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun create(article: Article): Mono&lt;Article&gt; =
  articleRepository.save(article)

fun findAll(): Flux&lt;Article&gt; =
  articleRepository.findAll()

fun findByTitleLike(name: String): Flux&lt;Article&gt; =
  articleRepository.findByTitleLikeCaseInsensitive(name)</pre>
<p>As we can clearly see, they are responsible for invoking appropriate functions from our repository and returning either <strong>Flux (multiple)</strong> or <strong>Mono (single)</strong> Article(s).</p>
<h3>7.2. findById()</h3>
<p>Nextly, let&#8217;s see the function responsible for fetching articles with their identifiers:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findById(id: String): Mono&lt;Article&gt; =
  articleRepository.findById(id)
    .switchIfEmpty(
      Mono.error(
        HttpStatusException(HttpStatus.NOT_FOUND, "Article with id: $id does not exists.")
      )
    )
</pre>
<p>Right here, we first invoke the <code class="EnlighterJSRAW" data-enlighter-language="generic">findById(id)</code> function from our repository. Then, it the article was found, we simply return the <code class="EnlighterJSRAW" data-enlighter-language="generic">Mono&lt;Article&gt;</code>.</p>
<p>Nevertheless, if the article does not exist in our MongoDB instance, the repository returns an <strong>empty Mono</strong>. And to handle this case, we use the <code class="EnlighterJSRAW" data-enlighter-language="generic">switchIfEmpty()</code> and translate it to the <strong>error Mono</strong>.</p>
<p>Finally, to return a <strong>meaningful status code and message</strong> to the user later, we use the <strong>HttpStatusException</strong>, which is a dedicated exception for that.</p>
<h3>7.3. update()</h3>
<p>Following, let&#8217;s take a look at the <strong>update()</strong> implementation:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun update(id: String, updated: Article): Mono&lt;Article&gt; =
  findById(id)
    .map { foundArticle -&gt; updated.copy(id = foundArticle.id) }
    .flatMap(articleRepository::update)</pre>
<p>Right here, we first try to fetch the article by id. If it was found, then we simply copy its identifier to the instance with updated fields called <strong>updated</strong>.</p>
<p>As the last step, we invoke the update method from our repository using the <strong>function reference</strong>.</p>
<blockquote><p><strong>Note:</strong>  Function reference is a great syntatic sugar here and is an equivalent of: <code class="EnlighterJSRAW" data-enlighter-language="generic">.flatMap{ articleRepository.update(it) }</code> , or <code class="EnlighterJSRAW" data-enlighter-language="generic">.flatMap{ updated -&gt; articleRepository.update(updated) }</code>.</p></blockquote>
<h3>7.4. deleteById()</h3>
<p>Lastly, let&#8217;s check the <strong>delete</strong> functionality:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun deleteById(id: String): Mono&lt;Void&gt; =
  findById(id)
    .flatMap(articleRepository::delete)
    .flatMap { deletedCount -&gt;
      if (deletedCount &gt; 0L)
        Mono.empty()
      else
        Mono.error(
          HttpStatusException(HttpStatus.NOT_FOUND, "Article with id: $id was not deleted.")
        )
    }
</pre>
<p>Similarly, we first try to find the article by id, and on success, we <strong>invoke the delete function from our repository</strong>.</p>
<p>As a result, we get a Long value indicating <strong>the number of affected (here- deleted) documents</strong>. So if this value is greater than 0, we simply return an <strong>empty Mono</strong>. Otherwise, we return an <strong>error Mono with HttpStatusException</strong>.</p>
<h2>8. Controller For Reactive Micronaut with MongoDB</h2>
<p>With all of that being done, let&#8217;s add the <strong>ArticleController</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Controller("/articles")
@ExecuteOn(TaskExecutors.IO)
class ArticleController(
  private val articleService: ArticleService
) {

  @Get
  fun findAll(): Flux&lt;Article&gt; =
    articleService.findAll()

  @Get("/{id}")
  fun findById(@PathVariable id: String): Mono&lt;Article&gt; =
    articleService.findById(id)

  @Post
  @Status(CREATED)
  fun create(@Body request: ArticleRequest): Mono&lt;Article&gt; =
    articleService.create(
      article = request.toArticle()
    )

  @Post("/search")
  fun search(@Body searchRequest: SearchRequest): Flux&lt;Article&gt; =
    articleService.findByTitleLike(
      name = searchRequest.title
    )

  @Put("/{id}")
  fun updateById(
    @PathVariable id: String,
    @Body request: ArticleRequest
  ): Mono&lt;Article&gt; =
    articleService.update(id, request.toArticle())

  @Delete("/{id}")
  @Status(NO_CONTENT)
  fun deleteById(@PathVariable id: String) =
    articleService.deleteById(id)

  private fun ArticleRequest.toArticle(): Article =
    Article(
      id = null,
      title = this.title,
      category = this.category,
      author = this.author
    )
}
</pre>
<p>As we can clearly see, we have to mark our controller class with the <strong>@Controller</strong> annotation and specify the base URI &#8211; <code class="EnlighterJSRAW" data-enlighter-language="generic">/articles</code> in our case.</p>
<p>Moreover, we make use of the <code class="EnlighterJSRAW" data-enlighter-language="generic">@ExecuteOn(TaskExecutors.IO)</code> <strong>to indicate which executor service a particular task should run on</strong>.</p>
<p>When it comes to particular functions, each one is responsible for handling different requests. Depending on which HTTP method should they respond to, we mark them with meaningful annotation: <strong>@Get</strong>, <strong>@Post</strong>, <strong>@Put</strong>, or <strong>@Delete</strong>. These annotations let us narrow down the URI route, like <code class="EnlighterJSRAW" data-enlighter-language="raw">/search</code>, or <code class="EnlighterJSRAW" data-enlighter-language="raw">/{id}</code>.</p>
<p>Additionally, we access path variables and request bodies with <strong>@PathVariable</strong> and <strong>@Body</strong> annotations (and DTOs implemented in paragraph 3), and set custom response codes with <strong>@Status</strong>. And of course, we have to remember that the status code can be changed with HttpStatusException in the service layer, as well.</p>
<h2>9. Validation</h2>
<p>At this point, our reactive Micronaut application can be run and we would be able to test the endpoints.</p>
<p>Nevertheless, before we do so, let&#8217;s add one more, crucial functionality: <strong>user input validation. </strong>In the following example, I will show you just a couple of possible validations, but please keep in mind that there are way more possibilities, which I encourage you to check out.</p>
<h3>9.1. Edit Controller</h3>
<p>As the first step, let&#8217;s make a few adjustments to the previously created controller:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">// Make class open
open class ArticleController

// Add @Valid annotation and make functions open, as well
open fun create(@Valid @Body request: ArticleRequest): Mono&lt;Article&gt;

open fun search(@Valid @Body searchRequest: SearchRequest): Flux&lt;Article&gt;

open fun updateById(@PathVariable id: String, @Valid @Body request: ArticleRequest): Mono&lt;Article&gt;
</pre>
<p>As we can clearly see, to trigger the validation process for particular request bodies, we have to add the <strong>@Valid</strong> annotation.</p>
<p>Moreover, functions with validation <strong>must be marked as open</strong>. Without that, our Micronaut app won&#8217;t compile with the following error:</p>
<blockquote><p>Method defines AOP advice but is declared final. Change the method to be non-final in order for AOP advice to be applied.</p></blockquote>
<h3>9.2. Change DTOs</h3>
<p>Nextly, let&#8217;s make a couple of changes to the <strong>ArticleRequest</strong>, <strong>SearchRequest</strong>, and <strong>Author</strong> classes:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Serializable
@Deserializable
data class Author(
  @field:NotBlank
  @field:Size(max = 50)
  val firstName: String,

  @field:NotBlank
  @field:Size(max = 50)
  val lastName: String,

  @field:Size(max = 50)
  @field:Email
  val email: String
)

@Introspected
data class ArticleRequest(
  @field:NotBlank val title: String,
  val category: ArticleCategory,
  @field:Valid val author: Author
)

@Introspected
data class SearchRequest(
  @field:NotBlank
  @field:Size(max = 50)
  val title: String
)
</pre>
<p>Firstly, we have to mark fields intended for validation with appropriate annotations, like <strong>@NotBlank</strong>, <strong>@Size</strong>, etc. These annotations are a part of the <code class="EnlighterJSRAW" data-enlighter-language="raw">javax.validation.constraints</code> package, which you might already know, for example from Spring Boot.</p>
<p>It&#8217;s worth mentioning two additional things we can see above: <strong>@Introspected</strong> and a <strong>@Valid</strong> annotation applied to the author.</p>
<p>With the first one, we state that the type should produce an <code class="EnlighterJSRAW" data-enlighter-language="raw">io.micronaut.core.beans.BeanIntrospection</code> at compilation time. Without it, Mirconaut won&#8217;t be able to process our annotations and all requests will fail at the runtime with the example message:</p>
<div>
<blockquote>
<div>request: Cannot validate com.codersee.model.dto.ArticleRequest. No bean introspection present.</div>
<div>Please add @Introspected to the class and ensure Micronaut annotation processing is enabled</div>
</blockquote>
<div>Additionally, we have to mark the author field with a <strong>@Valid</strong> annotation, so that it will be checked each time an ArticleRequest is validated. However, this time we don&#8217;t have to use an @Introspected as it is always used as a property of ArticleRequest, not a separate request body.</div>
</div>
<h2>10. Testing</h2>
<p>As the last step, we can finally run our reactive Micronaut application and check out if everything is working as expected with our MongoDB instance.</p>
<h3>10.1. Create a New Article</h3>
<p>Let&#8217;s start with creating a new Article:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request POST 'http://localhost:8080/articles' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "title": "Title 1",
    "category": "JAVA",
      "author": {
        "firstName": "First Name 1",
        "lastName": "Last Name 1",
        "email": "one@gmail.com"
      }
  }'

# Result when successful: 
Status Code: 201 Created
Response Body: 
{
  "id": "634f9e825b223f0572585007",
  "title": "Title 1",
  "category": "JAVA",
  "author":
    {
      "firstName": "First Name 1",
      "lastName": "Last Name 1",
      "email": "one@gmail.com"
    }
}

# Response when validations failed: 
Status: 400 Bad Request
Response Body:
{
  "message": "Bad Request",
  "_embedded": {
  "errors": [
    {
      "message": "request.author.email: must be a well-formed email address"
    },
    {
      "message": "request.author.firstName: must not be blank"
    },
    {
      "message": "request.author.lastName: size must be between 0 and 50"
    },
    {
      "message": "request.title: must not be blank"
    }
  ]
},
  "_links": {
    "self": {
      "href": "/articles",
      "templated": false
    }
  }
}</pre>
<p>As can be seen, everything is working correctly and validation works for both article and author payload.</p>
<h3>10.2. List All Articles</h3>
<p>Nextly, let&#8217;s list all articles:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request GET 'http://localhost:8080/articles'

# Response:
Status Code: 200 OK 
Example response body: 
[
  {
    "id": "634f9fff5b223f0572585008",
    "title": "Title 1",
    "category": "JAVA",
    "author": {
      "firstName": "First Name 1",
      "lastName": "Last Name 1",
      "email": "one@gmail.com"
    }
  }
]</pre>
<p>Everything is fine in this case.</p>
<h3>10.3. GET Article By Id</h3>
<p>Following, let&#8217;s get the details of the article by ID:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request GET 'http://localhost:8080/articles/634fa0f05b223f057258500a'

# Response:
Status Code: 200 OK 
Example response body when found: 
{
  "id": "634fa0f05b223f057258500a",
  "title": "Another 2",
  "category": "JAVA",
  "author": {
    "firstName": "First Name 1",
    "lastName": "Last Name 1",
    "email": "one@gmail.com"
  }
}

# Response when article not found:
{
  "message": "Not Found",
  "_embedded": {
    "errors": [
      {
        "message": "Article with id: 634fa0f05b223f057258500b does not exists."
      }
    ]
  },
  "_links": {
    "self": {
      "href": "/articles/634fa0f05b223f057258500b",
      "templated": false
    }
  }
}
</pre>
<p>Similarly, works flawlessly for both cases.</p>
<h3>10.4. Homework</h3>
<p>The rest of the endpoints will be your homework and I highly encourage you to check them all.</p>
<p>Below, you can find the remaining cURLs, which can be used for testing:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw"># Update endpoint:
curl --location --request PUT 'http://localhost:8080/articles/634fa0f05b223f057258500a' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "title": "Changed Title",
    "category": "JAVASCRIPT",
    "author": {
      "firstName": "First Name 4",
      "lastName": "Last Name 4",
      "email": "two@gmail.com"
    }
  }'

# Search endpoint:
curl --location --request POST 'http://localhost:8080/articles/search' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "title": "1"
  }'

# Delete endpoint:
curl --location --request DELETE 'http://localhost:8080/articles/634cf9d458a6a33cdd19fdab'
</pre>
<h2>10. Reactive Micronaut with MongoDB Summary</h2>
<p>And that would be all for this article on how to expose <strong>REST API using Reactive Micronaut with MongoDB</strong>.</p>
<p>As always, you can find the whole source code in this <a href="https://github.com/codersee-blog/micronaut-kotlin-mongodb-reactive" target="_blank" rel="noopener">GitHub repository</a>.</p>
<p>If you find this material helpful (or not) or would like to ask me about anything, please leave a comment in the section below. I always highly appreciate your feedback and I am happy to chat with you 😀</p>
<p>Take care and have a great week!</p>
<p>The post <a href="https://blog.codersee.com/reactive-rest-api-with-micronaut-and-mongodb/">Reactive REST API with Micronaut and MongoDB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/reactive-rest-api-with-micronaut-and-mongodb/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Micronaut with MongoDB and Kotlin Revisited [2022]</title>
		<link>https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/</link>
					<comments>https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 18 Oct 2022 05:30:07 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5503797</guid>

					<description><![CDATA[<p>In this revisited article, I will show you how to create a REST API using Micronaut with MongoDB and Kotlin (and data repositories, as well).</p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/">Micronaut with MongoDB and Kotlin Revisited [2022]</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2>1. Introduction</h2>
<p>Hi folks! In this step-by-step guide, I will show you how to create a REST API using <strong>Micronaut with MongoDB and Kotlin. </strong></p>
<p>As you might have noticed, this article is a revisited version of another blog post, which I created a long time ago. And before we start I would like to commend for upgrade suggestions made via <a href="https://www.softwareprophets.com/" target="_blank" rel="noopener">software development</a> work from Software Prophets. Without Eric&#8217;s comments, involvement and feedback, this article would never have been written. Thanks! 😀</p>
<p>Finally, I just wanted to admit that after this blog post, you will know precisely:</p>
<ul>
<li>how to spin up a <strong>MongoDB instance with Docker</strong>,</li>
<li>how to<strong> generate a new Micronaut project</strong> and add dependencies (like MongoDB) with ease,</li>
<li>how to persist and read data with <strong>MongoClient and data repositories for MongoDB</strong>,</li>
<li>how to expose <strong>REST endpoints</strong>.</li>
</ul>
<h2>2. New Micronaut with MongoDB and Kotlin Project</h2>
<p>As the first step, let&#8217;s generate a new Micronaut project with MongoDB dependencies. We can do it in three ways:</p>
<ul>
<li>using <a href="https://docs.micronaut.io/latest/guide/#cli" target="_blank" rel="noopener">Micronaut CLI</a>,</li>
<li>with cURL,</li>
<li>or with the <a href="https://micronaut.io/launch/" target="_blank" rel="noopener">Micronaut Launch page</a>.</li>
</ul>
<p>And although<strong> the CLI is a preferred way</strong> to generate new projects, for simplicity, let&#8217;s have a look at how to do it with the Launch page:</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5503803 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/10/micronaut-with-mongodb-and-kotlin-revisited-2022-launch-page-1024x501.png" alt="The screnshot show the Micronaut Launch page with settings used to generate project for this tutorial." width="800" height="391" srcset="https://blog.codersee.com/wp-content/uploads/2022/10/micronaut-with-mongodb-and-kotlin-revisited-2022-launch-page-1024x501.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/10/micronaut-with-mongodb-and-kotlin-revisited-2022-launch-page-300x147.png 300w, https://blog.codersee.com/wp-content/uploads/2022/10/micronaut-with-mongodb-and-kotlin-revisited-2022-launch-page-768x376.png 768w, https://blog.codersee.com/wp-content/uploads/2022/10/micronaut-with-mongodb-and-kotlin-revisited-2022-launch-page.png 1313w" sizes="auto, (max-width: 800px) 100vw, 800px" /></p>
<p>As we can see, this website provides us with a clickable interface, where we can specify what exactly should our project consist of.</p>
<p>To be on the same page and make sure that everything works, as expected, let&#8217;s have a look at the options I&#8217;ve chosen:</p>
<ul>
<li><strong>Application Type:</strong> Micronaut Application</li>
<li><strong>Micronaut Version:</strong> 3.7.1</li>
<li><strong>Java Version:</strong> 17</li>
<li><strong>Language:</strong> Kotlin</li>
<li><strong>Name:</strong> rest-mongodb (feel free to specify your own right here)</li>
<li><strong>Build Tool:</strong> Gradle Kotlin</li>
<li><strong>Base Package:</strong> com.codersee (similarly, you can pick whatever you want)</li>
<li><strong>Test Framework:</strong> JUnit</li>
<li><strong>Features:</strong> data-mongodb, mongo-sync (these two are important!)</li>
</ul>
<p>With all of that being selected, let&#8217;s click the Generate Project button, download the Zip package, and open up the project with our favorite IDE (IntelliJ in my case).</p>
<h2>3. Run MongoDB With Docker</h2>
<p>Nextly, let&#8217;s set up a new MongoDB instance, which we will use to connect to it later. I&#8217;ll do it with Docker, but if you would like to install it on your local machine, you can find more information on <a href="https://www.mongodb.com/docs/manual/installation/" target="_blank" rel="noopener">this, official MongoDB page</a>.<br />
&nbsp;<br />
<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><br />
&nbsp;<br />
So, firstly, let&#8217;s pull the Mongo image:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">docker pull mongo</pre>
<p>As the next step, let&#8217;s start a new container named <em>mongodb</em> in a detached mode:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">docker run -d -p 27017:27017 --name mongodb mongo</pre>
<p>As we can see, additionally we have to expose the container&#8217;s port <em>27017</em> to some port of our local machine (and I used the exact same port to make everything simpler).</p>
<p>To validate, we can use the <code class="EnlighterJSRAW" data-enlighter-language="raw">docker ps</code> command:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">CONTAINER ID IMAGE COMMAND                CREATED       STATUS       PORTS                    NAMES
ce86244c3fe1 mongo "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:27017-&gt;27017/tcp mongodb</pre>
<p>The above output indicates that our container is up and running and that the ports are mapped correctly.</p>
<h2>4. Edit application.yaml File</h2>
<p>After that is done, let&#8217;s get back to our project and edit the <code class="EnlighterJSRAW" data-enlighter-language="raw">mongodb.uri</code> property:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mongodb.uri: mongodb://localhost:27017/example</pre>
<p>As we can see, apart from pointing to the desired host and location, we specify the database name we would like to use- <code class="EnlighterJSRAW" data-enlighter-language="raw">example</code> in our case.</p>
<h2>5. Create Model Classes</h2>
<p>As the next step, let&#8217;s implement two classes we will use in our Micronaut app to exchange data with MongoDB:</p>
<ul>
<li><strong>AppUser</strong>&#8211; which will be responsible for transferring the data, like first and last name, email, and address of the application user,</li>
<li><strong>Address</strong>&#8211; which will be the inner class responsible for keeping user address information.</li>
</ul>
<p>Let&#8217;s start with the second one, the <strong>Address</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Serializable
@Deserializable
data class Address(
  val street: String,
  val city: String,
  val code: Int
)
</pre>
<p>As we can see, in order to work with MongoDB, we have to mark our data class with <strong>@Serializable</strong> and <strong>@Deserializable</strong> annotations. No other annotation is necessary here as this class will not be mapped to any collection.</p>
<p>Additionally, the Micronaut creators deserve recognition here, because even if we forget to do so, we will see an exception with a meaningful message, like:</p>
<blockquote><p>No serializable introspection present for type Address address. Consider adding Serdeable. Serializable annotate to type Address address. Alternatively if you are not in control of the project&#8217;s source code, you can use @SerdeImport(Address.class) to enable serialization of this type.</p></blockquote>
<p>Or:</p>
<blockquote><p>No deserializable introspection present for type: Address. Consider adding Serdeable.Deserializable annotate to type Address. Alternatively if you are not in control of the project&#8217;s source code, you can use @SerdeImport(Address.class) to enable deserialization of this type.</p></blockquote>
<p>After that, let&#8217;s add the <strong>AppUser</strong> data class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@MappedEntity
data class AppUser(
  @field:Id
  @field:GeneratedValue
  val id: String? = null,
  val firstName: String,
  val lastName: String,
  val email: String,
  val address: Address
)
</pre>
<p>This class, apart from the standard fields, has 4 features that are worth paying attention to:</p>
<ul>
<li><strong>@MappedEntity</strong> &#8211; an annotation used to mark a class as being persisted.</li>
<li><strong>@field:Id</strong> &#8211; simply the <strong>@Id</strong> annotation applied to the backing field of Kotlin property. With this one, we designate that this field is the ID of an entity.</li>
<li><strong>@field:GeneratedValue</strong>&#8211; similarly, the <strong>@GeneratedValue</strong> annotation indicates that the property will have a generated value.</li>
<li><strong>val id: String? = null</strong>&#8211;  a nullable id property, with a null set by default. Thanks to that, the ID will be generated automatically.</li>
</ul>
<h2>6. Create DTOs</h2>
<p>As the next step, let&#8217;s add DTOs to our Micronaut project.</p>
<p>Firstly, let&#8217;s implement the <strong>AppUserRequest</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">data class AppUserRequest(
  val firstName: String,
  val lastName: String,
  val email: String,
  val street: String,
  val city: String,
  val code: Int
)
</pre>
<p>This class will be used to create and update users later.</p>
<p>Moreover, let&#8217;s add the <strong>SearchRequest</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">data class SearchRequest(val name: String)
</pre>
<p>This one will be used later in search requests.</p>
<h2>7. Micronaut With MongoDB and Data Repositories</h2>
<p>As I mentioned in the introduction, in this Micronaut with MongoDB and Kotlin tutorial I would like to show you two different approaches on how to persist and fetch the data.</p>
<p>And in this paragraph, we&#8217;ll take a look at the <strong>synchronous data repositories for MongoDB</strong>.</p>
<h3>7.1. Implement AppUserRepository</h3>
<p>As the first step, let&#8217;s add the <strong>AppUserRepository</strong> interface:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@MongoRepository
interface AppUserRepository : CrudRepository&lt;AppUser, String&gt; 
</pre>
<p>As we can see, to make it a valid repository in terms of Micronaut, we have to mark our interface with the <strong>@MongoRepository</strong> annotation.</p>
<p>Additionally, we extend the generic <strong>CrudRepository </strong>providing the <strong>AppUser</strong> as the entity type and <strong>String</strong> as the ID type. As the name suggests, this repository let&#8217;s us perform CRUD operations, like save, update all, find, etc&#8230;</p>
<h3>7.2. Make Use Of Finder Methods</h3>
<p>Nextly, let&#8217;s make use of the finder methods feature. To put it simply, a finder method name will be translated to a valid query based on its name.</p>
<p>Let&#8217;s add the example definition to our interface:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findByFirstNameEquals(firstName: String): List&lt;AppUser&gt;</pre>
<p>As the name suggests, when using this function all users with a first name equal to the passed argument will be returned. If you would like to learn a bit more about this functionality, then please check out <a href="https://micronaut-projects.github.io/micronaut-data/latest/guide/#querying" target="_blank" rel="noopener">this link</a> to the documentation.</p>
<h3>7.3. Custom Query With @MongoFindQuery</h3>
<p>As the next step, let&#8217;s create a custom query with <strong>@MongoFindQuery</strong> annotation:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@MongoFindQuery("{ firstName: { \$regex: :name}}")
fun findByFirstNameLike(name: String): List&lt;AppUser&gt;</pre>
<p>This time, the users will be returned if their first name contains a given String value. It&#8217;s worth mentioning that this annotation lets us specify additional sorting, filtering, or custom fields projections, as well.</p>
<p>Of course, find is not the only supported operation and we can make use of other annotations to specify update, delete, or aggregate queries:</p>
<ul>
<li>@MongoUpdateQuery</li>
<li>@MongoDeleteQuery</li>
<li>@MongoAggregateQuery</li>
</ul>
<h3>7.4. Create AppUserService</h3>
<p>With all of the above being done, we have everything to implement the <strong>AppUserService</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class AppUserService(
  private val appUserRepository: AppUserRepository
) {

  fun create(userRequest: AppUserRequest): AppUser =
    appUserRepository.save(
      userRequest.toAppUserEntity()
    )

  fun findAll(): List&lt;AppUser&gt; =
    appUserRepository
      .findAll()
      .toList()

  fun findById(id: String): AppUser =
    appUserRepository.findById(id)
      .orElseThrow { HttpStatusException(HttpStatus.NOT_FOUND, "User with id: $id was not found.") }

  fun update(id: String, updateRequest: AppUserRequest): AppUser {
    val foundUser = findById(id)

    val updatedEntity =
      updateRequest
        .toAppUserEntity()
        .copy(id = foundUser.id)

    return appUserRepository.update(updatedEntity)
  }

  fun deleteById(id: String) {
    val foundUser = findById(id)

    appUserRepository.delete(foundUser)
  }

  fun findByNameLike(name: String): List&lt;AppUser&gt; =
    appUserRepository
      .findByFirstNameLike(name)

  private fun AppUserRequest.toAppUserEntity(): AppUser {
    val address = Address(
      street = this.street,
      city = this.city,
      code = this.code
    )

    return AppUser(
      id = null,
      firstName = this.firstName,
      lastName = this.lastName,
      email = this.email,
      address = address
    )
  }
}</pre>
<p>As can be seen, we mark our service with a <strong>@Singleton</strong> annotation to indicate that it should be instantiated only once.</p>
<p>Let&#8217;s have a few words about each function:</p>
<ul>
<li><strong>create(userRequest: AppUserRequest)</strong>&#8211; this one takes the AppUserRequest instance as an argument and invokes the <code class="EnlighterJSRAW" data-enlighter-language="kotlin">toAppUserEntity()</code> extension function returning a new AppUser instance. With this instance, we invoke the <code class="EnlighterJSRAW" data-enlighter-language="kotlin">save()</code> method returning the newly created object.</li>
<li><strong>findAll()</strong>&#8211; this function invokes the <code class="EnlighterJSRAW" data-enlighter-language="kotlin">findAll()</code> method from CrudRepository and converts returned Iterable to list.</li>
<li><strong>findById(id: String)</strong>&#8211; this one is a bit more interesting. Firstly, we invoke the <code class="EnlighterJSRAW" data-enlighter-language="generic">findById()</code> method returning an Optional of the AppUser. If it&#8217;s empty, we throw the <em>HttpStatusException</em>. In Micronaut, this class can be used to throw exceptions returning the desired status code and message. And that&#8217;s what we do here- as a result, the 400 Bad Request informing that the user with the given id was not found is returned.</li>
<li><strong>update(id: String, updateRequest: AppUserRequest)</strong>&#8211; as we can see, this one takes the desired id and an update request as the arguments. Firstly, we make use of already implemented <code class="EnlighterJSRAW" data-enlighter-language="generic">findById()</code> to fetch the desired user thus making sure he exists. Then, we simply convert our request to a new entity, but compared to the <code class="EnlighterJSRAW" data-enlighter-language="generic">create()</code>, have to set the id. Finally, we invoke the <code class="EnlighterJSRAW" data-enlighter-language="generic">update()</code> returning an updated user instance</li>
<li><strong>deleteById(id: String)</strong>&#8211; with this one we first make sure that the user exists and then, we invoke the <code class="EnlighterJSRAW" data-enlighter-language="generic">delete()</code> method of our repository.</li>
<li><strong>findByNameLike(name: String)</strong>&#8211; finally, this one is responsible for passing the name argument to our custom function: <code class="EnlighterJSRAW" data-enlighter-language="generic">findByFirstNameLike(name)</code>.</li>
</ul>
<h2>8. Add Controller</h2>
<p>As the last thing before we will be able to test our Micronaut with MongoDB and Kotlin application, let&#8217;s implement the <strong>AppUserController</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Controller("/users")
@ExecuteOn(TaskExecutors.IO)
class AppUserController(
    private val appUserService: AppUserService
) {

    @Get
    fun findAllUsers(): List&lt;AppUser&gt; =
        appUserService.findAll()

    @Get("/{id}")
    fun findById(@PathVariable id: String): AppUser =
        appUserService.findById(id)

    @Post
    @Status(CREATED)
    fun createUser(@Body request: AppUserRequest): AppUser =
        appUserService.create(request)

    @Post("/search")
    fun searchUsers(@Body searchRequest: SearchRequest): List&lt;AppUser&gt; =
        appUserService.findByNameLike(
            name = searchRequest.name
        )

    @Put("/{id}")
    fun updateById(
        @PathVariable id: String,
        @Body request: AppUserRequest
    ): AppUser =
        appUserService.update(id, request)

    @Delete("/{id}")
    @Status(NO_CONTENT)
    fun deleteById(@PathVariable id: String) =
        appUserService.deleteById(id)
}</pre>
<p>This time, everything starts with the <strong>@Controller</strong> annotation, where we put the base URI for this controller-<strong> &#8220;/users&#8221;</strong> in our case. As the name suggests, this annotation simply indicates that this class is a controller in our application.</p>
<p>Then, we specify the<strong> @ExecuteOn</strong> used to indicate which executor service use to run a particular task. In our case, we make use of the IO scheduler used to schedule I/O tasks (Thank you Captain Obvious 🙂 ) .</p>
<p>When it comes to the functions, we use the <strong>@Get</strong>, <strong>@Post</strong>, <strong>@Put</strong>, and <strong>@Delete</strong> annotations to specify what HTTP Methods they should handle and additional routes, like &#8220;/users/search&#8221;.  To bind a parameter from a path variable, we make use of the <strong>@PathVariable</strong> and <strong>@Body</strong> to do the same with a request body.</p>
<p>Finally, the <strong>@Status</strong> annotation is really useful to set the desired response code.</p>
<h2>9. Testing</h2>
<p>Let&#8217;s start by creating a new user with a POST request (and I highly recommend creating a few different users to better see if everything works, as expected):</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request POST 'localhost:8080/users' \
--header 'Content-Type: application/json' \
--data-raw '{
    "firstName": "Piotr",
    "lastName": "Wolak",
    "email": "contact@codersee.com",
    "street": "Some Street",
    "city": "Some city",
    "code": 1
}'

# Response: 
{
    "id": "633c6fb788e9024f42951f18",
    "firstName": "Piotr",
    "lastName": "Wolak",
    "email": "contact@codersee.com",
    "address": {
        "street": "Some Street",
        "city": "Some city",
        "code": 1
    }
}</pre>
<p>Secondly, let&#8217;s perform a GET request to see the list of all users:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request GET 'localhost:8080/users/'

# Example Response: 
[
    {
        "id": "633c6fb788e9024f42951f18",
        "firstName": "Piotr",
        "lastName": "Wolak",
        "email": "contact@codersee.com",
        "address": {
            "street": "Some Street",
            "city": "Some city",
            "code": 1
        }
    }
]</pre>
<p>Thirdly, let&#8217;s check if querying by ID works as expected in both cases:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request GET 'localhost:8080/users/633c6fb788e9024f42951f18'

# User found:
{
    "id": "633c6fb788e9024f42951f18",
    "firstName": "Piotr",
    "lastName": "Wolak",
    "email": "contact@codersee.com",
    "address": {
        "street": "Some Street",
        "city": "Some city",
        "code": 1
    }
}

# User not found:
# Status: 404 Not Found
# Response body:
{
    "message": "Not Found",
    "_embedded": {
        "errors": [
            {
                "message": "User with id: 633c6fb788e9024f42951f19 was not found."
            }
        ]
    },
    "_links": {
        "self": {
            "href": "/users/633c6fb788e9024f42951f19",
            "templated": false
        }
    }
}

</pre>
<p>Following, let&#8217;s perform an update:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request PUT 'localhost:8080/users/633c6fb788e9024f42951f18' \
--header 'Content-Type: application/json' \
--data-raw '{
    "firstName": "John",
    "lastName": "Doe",
    "email": "contact@codersee.com",
    "street": "Another Street",
    "city": "Another City",
    "code": 2
}'

# Response:
{
    "id": "633c6fb788e9024f42951f18",
    "firstName": "John",
    "lastName": "Doe",
    "email": "contact@codersee.com",
    "address": {
        "street": "Another Street",
        "city": "Another City",
        "code": 2
    }
}

# To verify, we can perform a GET request once again.
</pre>
<p>Before we check the DELETE endpoint, let&#8217;s make an example search query:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request POST 'localhost:8080/users/search' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Joh"
}
'

# Result: 
[
    {
        "id": "633c6fb788e9024f42951f18",
        "firstName": "John",
        "lastName": "Doe",
        "email": "contact@codersee.com",
        "address": {
            "street": "Another Street",
            "city": "Another City",
            "code": 2
        }
    }
]
</pre>
<p>Finally, let&#8217;s delete the user:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">curl --location --request DELETE 'localhost:8080/users/633c6fb788e9024f42951f18'

# Response:
# Status 204 No Content
# Response Body: empty

# If we repeat the request we should get: 
{
    "message": "Not Found",
    "_embedded": {
        "errors": [
            {
                "message": "User with id: 633c6fb788e9024f42951f18 was not found."
            }
        ]
    },
    "_links": {
        "self": {
            "href": "/users/633c6fb788e9024f42951f18",
            "templated": false
        }
    }
}
</pre>
<p>As we can clearly see, everything is working, as expected.</p>
<h2>10. Micronaut With MongoDB and MongoClient</h2>
<p>Ok, so for now we&#8217;ve learned how to use Micronaut with MongoDB and Kotlin and data repositories for MongoDB.</p>
<p>Before we end this article, I would like to show you one more way to interact with a Mongo database- the <strong>MongoClient</strong>. According to the documentation, <em>MongoClient</em> is:</p>
<blockquote><p>A client-side representation of a MongoDB cluster. Instances can represent either a standalone MongoDB instance, a replica set, or a sharded cluster. Instance of this class are responsible for maintaining an up-to-date state of the cluster, and possibly cache resources related to this, including background threads for monitoring, and connection pools.</p></blockquote>
<p>Simply put- this class lets us operate on a MongoDB cluster. In our case, we will use it to perform operations on the <strong>example</strong> database and <strong>app_user</strong> collection.</p>
<h2>11. Add AppUserWithMongoClientService</h2>
<p>Although you probably don&#8217;t want to name classes in your project this way, I&#8217;d like to distinguish somehow between these two approaches 😀</p>
<p>With that being said, let&#8217;s implement the <strong>AppUserWithMongoClientService</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class AppUserWithMongoClientService(
    private val mongoClient: MongoClient
) {

    fun create(userRequest: AppUserRequest): BsonValue {
        val createResult = getCollection()
            .insertOne(userRequest.toAppUserEntity())

        return createResult.insertedId
            ?: throw HttpStatusException(HttpStatus.BAD_REQUEST, "User was not created.")
    }

    fun findAll(): List&lt;AppUser&gt; =
        getCollection()
            .find()
            .toList()

    fun findById(id: String): AppUser =
        getCollection()
            .find(
                Filters.eq("_id", ObjectId(id))
            )
            .toList()
            .firstOrNull()
            ?: throw HttpStatusException(HttpStatus.NOT_FOUND, "User with id: $id was not found.")

    fun update(id: String, updateRequest: AppUserRequest): AppUser {
        val updateResult = getCollection()
            .replaceOne(
                Filters.eq("_id", ObjectId(id)),
                updateRequest.toAppUserEntity()
            )

        if (updateResult.modifiedCount == 0L)
            throw HttpStatusException(HttpStatus.BAD_REQUEST, "User with id: $id was not updated.")

        return findById(id)
    }

    fun deleteById(id: String) {
        val deleteResult = getCollection()
            .deleteOne(
                Filters.eq("_id", ObjectId(id))
            )

        if (deleteResult.deletedCount == 0L)
            throw HttpStatusException(HttpStatus.BAD_REQUEST, "User with id: $id was not deleted.")
    }

    fun findByNameLike(name: String): List&lt;AppUser&gt; =
        getCollection()
            .find(
                Filters.regex("firstName", name)
            )
            .toList()


    private fun getCollection(): MongoCollection =
        mongoClient
            .getDatabase("example")
            .getCollection("app_user", AppUser::class.java)

    private fun AppUserRequest.toAppUserEntity(): AppUser {
        val address = Address(
            street = this.street,
            city = this.city,
            code = this.code
        )

        return AppUser(
            id = null,
            firstName = this.firstName,
            lastName = this.lastName,
            email = this.email,
            address = address
        )
    }

}
</pre>
<p>Just like previously, we can see that we annotated the class as a <strong>@Singleton</strong> and reuse the <code class="EnlighterJSRAW" data-enlighter-language="generic">toAppUserEntity()</code> extension function.</p>
<p>As you might have noticed, this time we added the <code class="EnlighterJSRAW" data-enlighter-language="generic">getCollection()</code> function responsible for getting a <strong>MongoCollection&lt;AppUser&gt;</strong> instances. This class lets us utilize methods, like <code class="EnlighterJSRAW" data-enlighter-language="generic">find()</code>, <code class="EnlighterJSRAW" data-enlighter-language="generic">insertOne()</code>, or <code class="EnlighterJSRAW" data-enlighter-language="generic">deleteOne()</code>.</p>
<p>Moreover, in every function except the <code class="EnlighterJSRAW" data-enlighter-language="generic">create()</code> we make use of the <strong>Filters</strong> class, which is basically a factor for query filters.</p>
<h2>12. Implement AppUserWithMongoClientService</h2>
<p>And the last thing we will have to do in our Micronaut with MongoDB and Kotlin application is to add the <strong>AppUserWithMongoClientController</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Controller("/users-client")
@ExecuteOn(TaskExecutors.IO)
class AppUserWithMongoClientController(
  private val appUserWithMongoClientService: AppUserWithMongoClientService
) {

  @Get
  fun findAllUsers(): List&lt;AppUser&gt; =
    appUserWithMongoClientService.findAll()

  @Get("/{id}")
  fun findById(@PathVariable id: String): AppUser =
    appUserWithMongoClientService.findById(id)

  @Post
  @Status(CREATED)
  fun createUser(@Body request: AppUserRequest): HttpResponse&lt;Void&gt; {
    val createdId = appUserWithMongoClientService.create(request)

    return HttpResponse.created(
      URI.create(
        createdId.asObjectId().value.toHexString()
      )
    )
  }

  @Post("/search")
  @Status(CREATED)
  fun searchUser(@Body searchRequest: SearchRequest): List&lt;AppUser&gt; =
    appUserWithMongoClientService.findByNameLike(
      name = searchRequest.name
    )

  @Put("/{id}")
  fun updateById(
    @PathVariable id: String,
    @Body request: AppUserRequest
  ): AppUser =
    appUserWithMongoClientService.update(id, request)

  @Delete("/{id}")
  @Status(NO_CONTENT)
  fun deleteById(@PathVariable id: String) =
    appUserWithMongoClientService.deleteById(id)

}
</pre>
<p>As we can see, this implementation is almost identical compared to the previous controller. Worth noting are two things:</p>
<ul>
<li>to avoid URI conflicts, this one responds to the <code class="EnlighterJSRAW" data-enlighter-language="raw">/users-client</code>,</li>
<li>the createUser functionality works a bit differently- instead of returning the created entity, we make use of the <strong>201 Created</strong> status code and pass the id of the created object as the <code class="EnlighterJSRAW" data-enlighter-language="raw">location</code> header. That wasn&#8217;t necessary, but I wanted to show you another approach, as well 😀</li>
</ul>
<h2>13. Test Additional Endpoints</h2>
<p>Finally, we can test our new endpoints and make sure everything runs smoothly.</p>
<p>Nevertheless, I will not duplicate paragraph 10 right here, so this one will be your homework. There are two differences you have to remember about when testing this new controller, but if you&#8217;ve read the article thoroughly, I am pretty sure you will know exactly what I&#8217;m talking about.</p>
<h2>14. Micronaut with MongoDB and Kotlin Revisited Summary</h2>
<p>And that would be all for this article about <strong>Micronaut with MongoDB and Kotlin</strong>. If you&#8217;d like to see the whole source code, then please visit <a href="https://github.com/codersee-blog/micronaut-kotlin-mongodb-revisited" target="_blank" rel="noopener">this GitHub repository</a>.</p>
<p>Let me know if this article was useful for you in the comment section (or through the <a href="https://codersee.com/contact/">contact form</a>&#8211; if that suits you best). Of course, if you feel this post is a total crap and do not recommend it- please leave a comment, as well- always happy to chat and open for criticism 🙂</p>
<p>Have a great day!</p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/">Micronaut with MongoDB and Kotlin Revisited [2022]</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/micronaut-with-mongodb-and-kotlin-revisited-2022/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Micronaut with MongoDB and Kotlin</title>
		<link>https://blog.codersee.com/micronaut-with-mongodb-and-kotlin/</link>
					<comments>https://blog.codersee.com/micronaut-with-mongodb-and-kotlin/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 22 Dec 2020 09:35:58 +0000</pubDate>
				<category><![CDATA[Micronaut]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1729</guid>

					<description><![CDATA[<p>In this article, I will walk you step by step through the process of creating a simple REST API using Micronaut with MongoDB and Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin/">Micronaut with MongoDB 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>Hello dear readers. In this article, I would like to teach you how to create a <strong>Micronaut project with MongoDB and Kotlin</strong>.</p>
<p><a href="https://micronaut.io/" target="_blank" rel="noopener">Micronaut</a> is a modern, JVM-based framework for building modular, easily testable microservice and serverless applications. If you have any prior experience with reflection-based frameworks, like Spring or Spring Boot, you probably noticed that the startup time and memory consumption are bound to the size of the codebase. With Micronaut, these problems have been solved using Java’s annotation processors, making it a really great choice for low memory-footprint environments, like microservices.</p>
<p>Moreover, the creators took a lot of inspiration from Spring and Grails, so if you&#8217;ve ever worked with any of them, you&#8217;ll see plenty of similarities.</p>
<p>In this guide, we will create a simple CRUD REST API, which will allow the user to operate on the data from MongoDB (for a similar project in Spring Boot, please see <a href="http://codersee.com/spring-boot-mongodb-rest-api-crud-with-kotlin/" target="_blank" rel="noopener">this article</a>).</p>
<blockquote><p>Note: I&#8217;ve released a newer, refreshed version of this article, which you can find <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin-revisited-2022/">right here</a>.</p></blockquote>
<h2 class="article-heading-main">2. Run MongoDB Server</h2>
<p>Just like in previous articles, 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                  CREALTED             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. Set Up The Project</h2>
<h3 class="article-heading-sub">3.1. Generate The Project</h3>
<p>As the first step, let&#8217;s generate the project. We can do that either by installing the <a href="https://micronaut-projects.github.io/micronaut-starter/latest/guide/#installation" target="_blank" rel="noopener">Micronaut CLI</a> or with the <a href="https://micronaut.io/launch/" target="_blank" rel="noopener">online launcher</a> (similar to the Spring Initializr). In my case, I&#8217;ve generated it using the web launcher, setting the following values:</p>
<ul>
<li>Application Type: Micronaut Application</li>
<li>Java Version: 11</li>
<li>Base Package: com.codersee</li>
<li>Name: example</li>
<li><label class="input-field">Micronaut Version: 2.2.1</label></li>
<li>Language: Kotlin</li>
<li>Build: Gradle Kotlin</li>
<li>Test Framework: Junit</li>
<li>Included Features: mongo-sync</li>
</ul>
<p>Please notice, that we&#8217;ve included the<strong> mongo-sync</strong> feature, which will provide the support for the Mongo Synchronous Driver in our application.</p>
<h3 class="article-heading-sub">3.2. Add The No-arg Compiler Plugin</h3>
<p>Additionally, let&#8217;s add the <strong>no-arg compiler plugin</strong> inside the <strong>build.gradle.kts</strong> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">plugins {
    id("org.jetbrains.kotlin.plugin.noarg") version "1.4.10"
}
</pre>
<p>The <strong>no-arg compiler plugin</strong> generates an additional zero-argument constructor for classes with a specific annotation. I&#8217;ll show you, how to create such annotation and why should we use it in the next chapters.</p>
<h3 class="article-heading-sub">3.3. Configure application.yml</h3>
<p>As the next step, let&#8217;s the MongoDB connection config inside the <strong>application.yml</strong> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">micronaut:
  application:
    name: example
mongodb:
  uri: 'mongodb://localhost:27017/example'
</pre>
<p>This configuration instructs the MongoDB driver, that it should connect to the <em><strong>example</strong></em> database of the MongoDB instance running on port <strong>27017</strong>.</p>
<h2 class="article-heading-main">4. Create @NoArg annotation</h2>
<p>As the next step, let&#8217;s utilize the <strong>no-arg</strong> plugin and create the <strong>NoArg.kt</strong> file under the <em>annotation</em> package:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class NoArg
</pre>
<p>Additionally, we have to add the following configuration inside the <strong>build.gradle.kts</strong> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">noArg {
    annotation("com.codersee.annotation.NoArg")
}
</pre>
<p>Please keep in mind, that the parameter passed to the function,<em> &#8220;com.codersee.annotation.NoArg&#8221;</em> in my case, needs to match with the package inside your project.</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">@NoArg
data class Company(
    var id: ObjectId? = null,
    var name: String,
    var address: String
)
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@NoArg
data class Employee(
    var id: ObjectId? = null,
    var firstName: String,
    var lastName: String,
    var email: String,
    var company: Company?,
)
</pre>
<p>As you might have noticed, both classes have been annotated with the <strong>@NoArg</strong>. Without it, we would have to make all of the fields nullable and that&#8217;s something I wanted to avoid.</p>
<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.</p>
<h2 class="article-heading-main">6. Add Custom Exception With Status Code</h2>
<p>Nextly, let’s create a custom exception class named <strong>NotFoundException </strong>and <strong>ExceptionResponse,</strong> which will be used to serialize the response:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class NotFoundException(msg: String) : RuntimeException(msg)
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class ExceptionResponse(
    val message: String?
)
</pre>
<p><strong>NotFoundException</strong> is just a simple class extending the <strong>RuntimeException</strong>. Additionally, this class contains the <em><strong>msg</strong></em> field, which will be used to set and obtain the custom message.</p>
<p>With that being done, let&#8217;s create the <strong>NotFoundExceptionHandler</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Produces
@Singleton
class NotFoundExceptionHandler :
    ExceptionHandler&lt;NotFoundException, HttpResponse&lt;ExceptionResponse&gt;&gt; {

    override fun handle(
        request: HttpRequest&lt;*&gt;,
        exception: NotFoundException
    ): HttpResponse&lt;ExceptionResponse&gt; =
        HttpResponse.notFound(
            ExceptionResponse(
                message = exception.message
            )
        )

}
</pre>
<p>As you can see, we implement the ExceptionHandler interface and specify two things:</p>
<ul>
<li>The <strong>Throwable</strong>, we&#8217;d like to handle- <strong>NotFoundException</strong> in our case</li>
<li>The result type- in our case, the <strong> HttpResponse&lt;ExceptionResponse&gt;</strong></li>
</ul>
<p>Moreover, we provide the declaration for the <em><strong>handle</strong></em> function, in which we set the response body with the message from the exception and the<strong> 404 NOT FOUND</strong> response code.</p>
<h3 class="article-heading-sub">6.1. What is @Singleton and @Produces?</h3>
<p>Well, the <strong>@Signleton</strong> is used to register a Singleton in Micronaut’s application context (just like a @Component in Spring Boot). The <strong>@Produces</strong> annotation, on the other hand, is used to indicate, what MediaTypes will be produced by our component (by default, it&#8217;s set to the application/json).</p>
<h2 class="article-heading-main">7. Create Repositories</h2>
<p>As the next step, let’s create repositories for our data. In our project, we&#8217;ll use the synchronous <strong>MongoClient </strong>to perform operations on the MongoDB<strong>.</strong></p>
<h3 class="article-heading-sub">7.1. Implement CompanyRepository</h3>
<p>Let’s start by adding the <strong>CompanyRepository</strong> first:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class CompanyRepository(
    private val mongoClient: MongoClient
) {

    fun create(company: Company): InsertOneResult =
        getCollection()
            .insertOne(company)

    fun findAll(): List&lt;Company&gt; =
        getCollection()
            .find()
            .toList()

    fun findById(id: String): Company? =
        getCollection()
            .find(
                Filters.eq("_id", ObjectId(id))
            )
            .toList()
            .firstOrNull()

    fun update(id: String, update: Company): UpdateResult =
        getCollection()
            .replaceOne(
                Filters.eq("_id", ObjectId(id)),
                update
            )

    fun deleteById(id: String): DeleteResult =
        getCollection()
            .deleteOne(
                Filters.eq("_id", ObjectId(id))
            )

    private fun getCollection() =
        mongoClient
            .getDatabase("example")
            .getCollection("company", Company::class.java)
}
</pre>
<p>Let&#8217;s discuss, what exactly is happening in this class.</p>
<p>As the first step, we need to register it in the application context with <strong>@Signleton</strong> and inject the <strong>MongoClient</strong> instance through the constructor.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class CompanyRepository(
    private val mongoClient: MongoClient
) 
</pre>
<p>Nextly, we create the helper function, which will be responsible for getting the MongoCollection object of the <strong>company</strong> collection from the <em><strong>example</strong></em> database. Moreover, we pass the <strong>Company::class.java </strong>as the second parameter, so that all of the returned documents will be cast to this type.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">private fun getCollection() =
    mongoClient
        .getDatabase("example")
        .getCollection("company", Company::class.java)
</pre>
<p>Following, let&#8217;s look at the functions responsible for getting the data from the database:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findAll(): List&lt;Company&gt; =
    getCollection()
        .find()
        .toList()

fun findById(id: String): Company? =
    getCollection()
        .find(
            Filters.eq("_id", ObjectId(id))
        )
        .toList()
        .firstOrNull()
</pre>
<p>As can be seen, in both cases the <em><strong>find</strong><strong>()</strong></em> function has been used, returning the iterable, which we convert to the List. But, when looking for a specific company, we apply the additional filter looking for a document with the specified ID and either return the Company or null.</p>
<p>Lastly, let&#8217;s look at functions responsible for creating, updating, and deleting the data:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun create(company: Company): InsertOneResult =
    getCollection()
        .insertOne(company)

fun update(id: String, update: Company): UpdateResult =
    getCollection()
        .replaceOne(
            Filters.eq("_id", ObjectId(id)),
            update
        )

fun deleteById(id: String): DeleteResult =
    getCollection()
        .deleteOne(
            Filters.eq("_id", ObjectId(id))
        )
}
</pre>
<p>As you can see, all of them return<strong> _Result</strong> instances. These classes contain a lot of useful information, like the count, or ID of affected documents (which we will use in the service layer).</p>
<h3 class="article-heading-sub">7.2. Implement EmployeeRepository</h3>
<p>Similarly, let&#8217;s implement the <strong>EmployeeRepository</strong>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class EmployeeRepository(
    private val mongoClient: MongoClient
) {

    fun create(employee: Employee): InsertOneResult =
        getCollection()
            .insertOne(employee)

    fun findAll(): List =
        getCollection()
            .find()
            .toList()

    fun findAllByCompanyId(companyId: String): List&lt;Employee&gt; =
        getCollection()
            .find(
                Filters.eq("company._id", ObjectId(companyId))
            )
            .toList()


    fun findById(id: String): Employee? =
        getCollection()
            .find(
                Filters.eq("_id", ObjectId(id))
            )
            .toList()
            .firstOrNull()

    fun update(id: String, update: Employee): UpdateResult =
        getCollection()
            .replaceOne(
                Filters.eq("_id", ObjectId(id)),
                update
            )

    fun deleteById(id: String): DeleteResult =
        getCollection()
            .deleteOne(
                Filters.eq("_id", ObjectId(id))
            )

    private fun getCollection() =
        mongoClient
            .getDatabase("example")
            .getCollection("employee", Employee::class.java)
}
</pre>
<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’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’s create the <strong>CompanyService</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Singleton
class CompanyService(
    private val companyRepository: CompanyRepository,
    private val employeeRepository: EmployeeRepository
) {

    fun createCompany(request: CompanyRequest): BsonValue? {
        val insertedCompany = companyRepository.create(
            Company(
                name = request.name,
                address = request.address
            )
        )
        return insertedCompany.insertedId
    }

    fun findAll():  List&lt;Company&gt; {
        return companyRepository.findAll()
    }

    fun findById(id: String): Company {
        return companyRepository.findById(id)
            ?: throw NotFoundException("Company with id $id was not found")
    }

    fun updateCompany(id: String, request: CompanyRequest): Company {
        val updateResult = companyRepository.update(
            id = id,
            update = Company(name = request.name, address = request.address)
        )

        if (updateResult.modifiedCount == 0L)
            throw throw RuntimeException("Company with id $id was not updated")

        val updatedCompany = findById(id)
        updateCompanyEmployees(updatedCompany)

        return updatedCompany
    }

    fun deleteById(id: String) {
        val deleteResult = companyRepository.deleteById(id)

        if (deleteResult.deletedCount == 0L)
            throw throw RuntimeException("Company with id $id was not deleted")
    }

    private fun updateCompanyEmployees(updatedCompany: Company) {
        employeeRepository
            .findAllByCompanyId(updatedCompany.id!!.toHexString())
            .map {
                employeeRepository.update(
                    it.id!!.toHexString(),
                    it.apply { it.company = updatedCompany }
                )
            }
    }
}
</pre>
<p>Basically, these are just simple functions responsible for creating, deleting, and retrieving the data through the <strong>CompanyRepository</strong>.</p>
<p>However, you might have noticed that each time, we would like to update the company, <strong>we need to update all employees connected with it</strong>. 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 relationships, please let me know and I will create another tutorial about it :).</p>
<h3 class="article-heading-sub">8.2. Implement EmployeeService</h3>
<p>In the same fashion, let&#8217;s create the <strong>EmployeeRequest</strong> responsible for data handling and the <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">@Singleton
class EmployeeService(
    private val employeeRepository: EmployeeRepository,
    private val companyService: CompanyService

) {

    fun createEmployee(request: EmployeeRequest): BsonValue? {
        val company = request.companyId?.let { companyService.findById(it) }

        val insertedEmployee = employeeRepository.create(
            Employee(
                firstName = request.firstName,
                lastName = request.lastName,
                email = request.email,
                company = company
            )
        )
        return insertedEmployee.insertedId
    }

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

    fun findById(id: String): Employee =
        employeeRepository.findById(id)
            ?: throw NotFoundException("Employee with id $id not found")

    fun updateEmployee(id: String, request: EmployeeRequest): Employee {
        val foundCompany = request.companyId?.let { companyService.findById(it) }

        val updateResult = employeeRepository.update(
            id = id,
            update = Employee(
                firstName = request.firstName,
                lastName = request.lastName,
                email = request.email,
                company = foundCompany
            )
        )

        if (updateResult.modifiedCount == 0L)
            throw throw RuntimeException("Employee with id $id was not updated")

        return findById(id)
    }

    fun deleteById(id: String) {
        val deleteResult = employeeRepository.deleteById(id)

        if (deleteResult.deletedCount == 0L)
            throw throw RuntimeException("Company with id $id was not deleted")
    }
}
</pre>
<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>
<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 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>
<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>Let&#8217;s add the <strong>CompanyController</strong> class to our project first:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Controller("/api/company")
class CompanyController(
    private val companyService: CompanyService
) {

    @Post
    fun create(@Body request: CompanyRequest): HttpResponse&lt;Void&gt; {
        val createdId = companyService.createCompany(request)

        return HttpResponse.created(
            URI.create(
                createdId!!.asObjectId().value.toHexString()
            )
        )
    }

    @Get
    fun findAll(): HttpResponse&lt;List&lt;CompanyResponse&gt;&gt; {
        val companies = companyService
            .findAll()
            .map { CompanyResponse.fromEntity(it) }

        return HttpResponse.ok(companies)
    }

    @Get("/{id}")
    fun findById(@PathVariable id: String): HttpResponse&lt;CompanyResponse&gt; {
        val company = companyService.findById(id)

        return HttpResponse.ok(
            CompanyResponse.fromEntity(company)
        )
    }

    @Put("/{id}")
    fun update(
        @PathVariable id: String,
        @Body request: CompanyRequest
    ): HttpResponse&lt;CompanyResponse&gt; {
        val updatedCompany = companyService.updateCompany(id, request)

        return HttpResponse.ok(
            CompanyResponse.fromEntity(updatedCompany)
        )
    }

    @Delete("/{id}")
    fun deleteById(@PathVariable id: String): HttpResponse&lt;Void&gt; {
        companyService.deleteById(id)

        return HttpResponse.noContent()
    }
}
</pre>
<p>As you can see,  in order to make the class a controller inside the Micronaut context, we need to annotate it with <em><strong>@Controller</strong></em>. Additionally, we can pass the base URI to it, as we&#8217;ve done in the above code snippet.</p>
<p>Basically, the <strong>CompanyController</strong> utilizes already prepared functions from the <strong>CompanyService</strong>. Each function needs to be marked with appropriate annotation (like @Get, @Put, etc.) in order to serve as an HTTP request handler. By default, it will use the base URI set for the class (/api/company in our case).</p>
<p>As the last thing, please notice the <strong>@PathVariable- </strong>used to bind the parameter of the function exclusively from the path variable- and the<strong> @Body</strong>&#8211; indicating that the method argument is bound from the HTTP body.</p>
<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">@Controller("/api/employee")
class EmployeeController(
    private val employeeService: EmployeeService
) {

    @Post
    fun create(@Body request: EmployeeRequest): HttpResponse {
        val createdId = employeeService.createEmployee(request)

        return HttpResponse.created(
            URI.create(
                createdId!!.asObjectId().value.toHexString()
            )
        )
    }

    @Get
    fun findAll(): HttpResponse&lt;List&gt; {
        val employees = employeeService
            .findAll()
            .map { EmployeeResponse.fromEntity(it) }

        return HttpResponse.ok(employees)
    }

    @Get("/{id}")
    fun findById(@PathVariable id: String): HttpResponse {
        val employee = employeeService.findById(id)

        return HttpResponse.ok(
            EmployeeResponse.fromEntity(employee)
        )
    }

    @Put("/{id}")
    fun update(
        @PathVariable id: String,
        @Body request: EmployeeRequest
    ): HttpResponse {
        val updatedEmployee = employeeService.updateEmployee(id, request)

        return HttpResponse.ok(
            EmployeeResponse.fromEntity(updatedEmployee)
        )
    }

    @Delete("/{id}")
    fun deleteById(@PathVariable id: String): HttpResponse {
        employeeService.deleteById(id)

        return HttpResponse.noContent()
    }
}
</pre>
<h2 class="article-heading-main">10. Test with CURL</h2>
<p>Finally, we can run our application- for instance, with the <em>./gradlew run</em> command- 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>To validate, let&#8217;s list all companies:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">curl --location --request GET 'localhost:8080/api/company'

# Example output
[
  {
    "id": "5fcba07a30e3af4497f5de16",
    "name": "Company 1",
    "address": "Address 1"
  },
  {
    "id": "5fcba07c30e3af4497f5de17",
    "name": "Company 2",
    "address": "Address 2"
  }
]
</pre>
<p>If we would like to find a specific company, then let&#8217;s use the following command:</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 body with 204 No Content status and a company should be deleted as well.</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 creating the <strong>Micronaut project with MongoDB and Kotlin</strong></p>
<p>I hope, you really enjoyed it and that after reading it, you&#8217;ll get more interested in the Micronaut project. If you would like to ask about anything or add your own suggestions, 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>As usual, you can find the working project in this <a href="https://github.com/codersee-blog/kotlin-micronaut-mongodb-crud" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>The post <a href="https://blog.codersee.com/micronaut-with-mongodb-and-kotlin/">Micronaut with MongoDB 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/micronaut-with-mongodb-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>6</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-05-17 16:21:19 by W3 Total Cache
-->