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

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>MongoDB Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>MongoDB with Kotlin Coroutines in Ktor</title>
		<link>https://blog.codersee.com/mongodb-kotlin-coroutines-ktor/</link>
					<comments>https://blog.codersee.com/mongodb-kotlin-coroutines-ktor/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Wed, 26 Feb 2025 16:00:00 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[Coroutines]]></category>
		<category><![CDATA[Mongo]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=17012558</guid>

					<description><![CDATA[<p>In this lesson, we are going to learn how to work with MongoDB and coroutines using the MongoDB Kotlin Driver in Ktor.</p>
<p>The post <a href="https://blog.codersee.com/mongodb-kotlin-coroutines-ktor/">MongoDB with Kotlin Coroutines in Ktor</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In this article, I will show you how to work with MongoDB and Kotlin coroutines in a Ktor application. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Note: this article was created based on one of the services we implement in my <a href="https://codersee.com/courses/ktor-server-pro/">Ktor Server Pro course</a>. If you would like to learn how to expose a fully functional REST API, secure the application, and many many more, then don&#8217;t hesitate and secure your spot today😉</p>
</blockquote>



<p>Before we start, I just wanted to mention that at the moment of writing <strong>KMongo is officially deprecated! </strong>Maybe you saw my previous article, maybe you saw some outdated content in some other places. Either way, we should not use that library anymore in favor of the official driver that comes in two flavors:</p>



<ul class="wp-block-list">
<li><strong>MongoDB Kotlin Driver (for applications using coroutines)</strong></li>



<li>MongoDB Kotlin Sync Driver (for apps that require synchronous processing)</li>
</ul>



<h2 class="wp-block-heading" id="h-video-content">Video Content</h2>



<p>As always, if you prefer a video content, then please check out my latest YouTube video:</p>


<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><a href="https://blog.codersee.com/mongodb-kotlin-coroutines-ktor/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2F8NfdOv2R-4U%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /><figcaption></figcaption></figure>


<h2 class="wp-block-heading" id="h-create-amp-verify-mongodb-instance">Create &amp; Verify MongoDB Instance</h2>



<p>If you already have a MongoDB instance installed on your machine, feel free to skip this step.</p>



<p>On the other hand, if that is not the case, you can install it using the <a href="https://www.mongodb.com/docs/manual/installation/">official manual</a>. Or, if you just like me have a Docker environment, you can run the following command:</p>



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



<p>This way, we run the Mongo docker container and expose it&#8217;s port <code>27017</code> .</p>



<p>And to verify it running, we can run the <code>docker ps</code> :</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS                      NAMES
9ecd8986ac34   mongo:8.0.4   "docker-entrypoint.s…"   20 seconds ago   Up 19 seconds   0.0.0.0:27017->27017/tcp   my_awesome_mongo_name</pre>



<p>As we can see, it was created successfully and we should be able to connect to it at <code>localhost:27017</code></p>



<p>We can assert that, as well, for example with a free and official tool- <a href="https://www.mongodb.com/try/download/compass">MongoDB Compass</a>:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="599" src="http://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_verify_connection_compass-1024x599.png" alt="Image is a screenshot from MongoDB Compass application and shows how to establish a new connection to mongo instance." class="wp-image-17012563" srcset="https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_verify_connection_compass-1024x599.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_verify_connection_compass-300x176.png 300w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_verify_connection_compass-768x450.png 768w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_verify_connection_compass.png 1411w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>And if we are able to connect, it means that we can head to the next step. </p>



<h2 class="wp-block-heading" id="h-create-a-new-ktor-project">Create a New Ktor Project</h2>



<p>Nextly, let&#8217;s navigate to the <a href="https://start.ktor.io/" target="_blank" rel="noreferrer noopener">Ktor Project Generator</a> page to create a fresh project from scratch. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>If you use the IntelliJ Ultimate Edition, then you can generate it in your IDE</p>
</blockquote>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="468" src="http://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_create_project_in_generator-1024x468.png" alt="Image is a screenshot from Ktor Generator Page and shows necessary settings for our project." class="wp-image-17012564" srcset="https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_create_project_in_generator-1024x468.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_create_project_in_generator-300x137.png 300w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_create_project_in_generator-768x351.png 768w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_create_project_in_generator.png 1230w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>As we can see, we will be using <strong>Ktor 3.1.0</strong> with <strong>Netty</strong> and configuration in the <strong>YAML</strong> file.</p>



<p>The <strong>important </strong>thing to mention here is that if we want to work with Kotlin coroutines, <strong>we should not select the below MongoDB plugin:</strong></p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="373" src="http://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo-1024x373.png" alt="Image shows MongoDB plugin for Ktor that we should not use when working with coroutines. " class="wp-image-17012565" srcset="https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo-1024x373.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo-300x109.png 300w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo-768x279.png 768w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo.png 1223w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>Because as we can see, this adds the MongoDB Kotlin Sync Driver for synchronous processing:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="828" src="http://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo_preview-1024x828.png" alt="" class="wp-image-17012566" srcset="https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo_preview-1024x828.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo_preview-300x243.png 300w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo_preview-768x621.png 768w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_coroutines_project_generator_sync_mongo_preview.png 1224w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>So, in our case, to keep our Ktor application as vanilla as possible, we will not add any additional plugins. </p>



<p>With that done, let&#8217;s hit the <em>Download</em> button and let&#8217;s import the project to our IDE.</p>



<h2 class="wp-block-heading" id="h-add-mongodb-async-driver-to-ktor">Add MongoDB Async Driver to Ktor</h2>



<p>Before adding the necessary import, let&#8217;s perform a small cleanup. </p>



<p>Firstly, let&#8217;s remove the <code>Routing.kt</code> file- we don&#8217;t need it for this tutorial.</p>



<p>Then, let&#8217;s navigate to the <code>Application.kt</code> and let&#8217;s get rid of routing config, as well. Eventually, we should have something like that: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""> package com.codersee

import io.ktor.server.application.*

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

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



<p>With that done, let&#8217;s add the MongoDB Kotlin Driver to work with coroutines. </p>



<p>During the configuration, we decided to use the Gradle version catalog. So now, we must navigate to the <code>libs.versions.toml</code> inside the <code>gradle</code> directory and add the <code>mongodb-version</code> along with <code>mongodb-driver-kotlin</code>:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[versions]
kotlin-version = "2.1.10"
ktor-version = "3.1.0"
logback-version = "1.4.14"
mongodb-version = "5.3.1"

[libraries]
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor-version" }
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor-version" }
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-version" }
ktor-server-config-yaml = { module = "io.ktor:ktor-server-config-yaml", version.ref = "ktor-version" }
ktor-server-test-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor-version" }
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin-version" }
mongodb-driver-kotlin = { module = "org.mongodb:mongodb-driver-kotlin-coroutine", version.ref = "mongodb-version" }

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-version" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor-version" }</pre>



<p>With that done, let&#8217;s navigate to <code>build.gradle.kts</code> and add the following line: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">implementation(libs.mongodb.driver.kotlin)</pre>



<p>Lastly, let&#8217;s sync the gradle project so that the necessary libraries are fetched. </p>



<h2 class="wp-block-heading" id="h-introduce-model-classes">Introduce Model Classes</h2>



<p>Before we can start working with coroutines, we must prepare classes that will be translated into Mongo documents and vice versa. </p>



<p>To do so, let&#8217;s create the <code>Product.kt</code> class and put the following:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import org.bson.codecs.pojo.annotations.BsonId
import org.bson.types.ObjectId

data class Product(
    @BsonId
    val id: ObjectId? = null,
    val name: String,
    val description: String,
    val price: Double,
    val category: ProductCategory,
    val tags: List&lt;ProductTag>,
)

enum class ProductCategory {
    VIDEO_GAMES, TOOLS, HOME_AND_KITCHEN, FOOD
}

enum class ProductTag {
    EXCLUSIVE, HANDMADE, ORGANIC, BESTSELLER
}</pre>



<p>As we can see, all fields except the <em>id</em> field are plain Kotlin classes. And later, they will be serialized/deserialized 1:1 when saving and retrieving from the database. </p>



<p>When it comes to the <code>id</code> field, we mark it using the <code>@BsonId</code> annotation. Thanks to that, it will be serialized to the <code>_id</code> BSON field that represents a primary key of our document. Moreover, we assign a default null value to it. This way, the value will be generated automatically. </p>



<h2 class="wp-block-heading" id="h-configure-ktor-connection-to-mongodb">Configure Ktor Connection to MongoDB</h2>



<p>As the next step, let&#8217;s connect our Ktor application to the Mongo instance. </p>



<p>And for that purpose, let&#8217;s navigate to <code>Application.kt</code> and implement the following logic: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun Application.module() {
    val settings = MongoClientSettings.builder()
        .applyConnectionString(
            ConnectionString("mongodb://localhost:27017")
        )
        .build()

    val client = MongoClient.create(settings)
    val database = client.getDatabase("application")
    val productCollection = database.getCollection&lt;Product>("products")
}</pre>



<p>Firstly, we instantiate the <em>MongoClientSettings</em> builder. A builder that allows us to configure the connection string for our database. Additionally, if you are looking for more configuration options, like reads or writes repetition, then you should start in there. </p>



<p>Then, we create a new client and pass our settings to it. If you would like to, then you could use another variant of the <code>create</code> function that takes the connection String as an argument:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">public fun create(connectionString: String): MongoClient</pre>



<p>But, in my opinion, using the builder approach is a better choice in terms of extensibility. </p>



<p>With that done, we get the instance of our database, by passing its name. Lastly, we obtain the MongoCollection that we will be injecting later into the product repository. Again, we pass the name of our collection, too.</p>



<p>To verify, let&#8217;s run our application.</p>



<p>As a result, we should see the following text in the logs indicating that everything is perfectly fine: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">[cluster-ClusterId{value='67bd5f4689251d25d5077fb7', description='null'}-localhost:27017] INFO  org.mongodb.driver.cluster - Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, cryptd=false, state=CONNECTED, ok=true, minWireVersion=0, maxWireVersion=25, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=26289200, minRoundTripTimeNanos=0}</pre>



<h2 class="wp-block-heading" id="h-mongodb-coroutines-crud-operations">MongoDB Coroutines CRUD Operations</h2>



<p>After we did all of that preparation, we can finally create the <code>ProductRepository</code> class:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class ProductRepository(
    private val productCollection: MongoCollection&lt;Product>,
) { }</pre>



<p>And inject the collection in <code>Application.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="">val productRepository = ProductRepository(productCollection)</pre>



<p>The constructor injection is a great way to make our code easier to test in the future. </p>



<p>Anyway, coming back to the topic, let&#8217;s learn how we can how we can perform basic CRUD operations with coroutines. </p>



<h3 class="wp-block-heading" id="h-persits-products">Persits Products</h3>



<p>Initially, our <code>products</code> collection is empty, so let&#8217;s add the following code to start populating it: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">suspend fun save(product: Product): Product? {
    val result = productCollection.insertOne(product)

    return result.insertedId
        ?.let { product.copy(id = it.asObjectId().value) }
}</pre>



<p>First of all, we must make our function <em>suspend</em>. Why? Because the <em>insertOne</em> we use is a suspend function, so we must invoke it either from the coroutine or another suspend function. </p>



<p>When it comes to the persisting- the function that we use returns the <code>InsertOneResult</code> that allows us to either get a boolean informing if the write was acknowledged or the generated identifier of the saved product. And in my opinion, reading that and returning a Product instance with the updated field is a quite nice approach. </p>



<p>After that, let&#8217;s get back to the <code>Application.kt</code> and test this function: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val saved = runBlocking {
    productRepository.save(
        Product(
            name = "Product 1",
            description = "Description 1",
            price = 19.99,
            category = ProductCategory.TOOLS,
            tags = listOf(ProductTag.EXCLUSIVE, ProductTag.BESTSELLER)
        )
    )
}

println(saved)</pre>



<p>I know, the good, old println 🤠 </p>



<p>Anyway, if we run our application, we should see the following in the logs: </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="">Product(id=67bd62f974edd96bbd59874a, name=Product 1, description=Description 1, price=19.99, category=TOOLS, tags=[EXCLUSIVE, BESTSELLER])</pre>



<p>Additionally, when we hit the <em>refresh</em> button in MongoDB Compass, we should see the following: </p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="358" src="http://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_created_product_atlas_view-1024x358.png" alt="Image shows a screenshot from MongoDB Compass and persisted product with coroutines." class="wp-image-17012580" srcset="https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_created_product_atlas_view-1024x358.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_created_product_atlas_view-300x105.png 300w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_created_product_atlas_view-768x269.png 768w, https://blog.codersee.com/wp-content/uploads/2025/02/ktor_mongodb_created_product_atlas_view.png 1410w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>And this proves that not only the Product was saved. But also, the <code>application</code> database and <code>products</code> collection was created by our client automatically. </p>



<h2 class="wp-block-heading" id="h-find-by-id">Find By ID</h2>



<p> Nextly, let&#8217;s implement the function to fetch product by identifier: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">suspend fun findById(id: String): Product? {
    val objectId = ObjectId(id)

    return productCollection.find(
        eq("_id", objectId)
    ).firstOrNull()
}</pre>



<p>This time, we use the <code>find</code> method. And this function returns the <em>FindFlow</em> which is the <em>Flow </em>implementation for find operations. </p>



<p>This function allows us to pass filters as arguments to it. And to get the particular product, we use the <strong>eq </strong>filter. One of the many filters that we can find in <code>com.mongodb.client.model.Filters</code>. We must remember that our identifier is of the <em>ObjectId</em> type, so we create a new instance from our String value. </p>



<p>Lastly, we invoke the <code>firstOrNull</code>&#8211; the terminal operator that returns the first element emitted by the flow and then cancels flow&#8217;s. We leverage the fact that only one element with such an identifier can be found in our database.</p>



<p>So with all of that done, let&#8217;s get back to the <code>Application.kt</code> and test this function: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val found = runBlocking {
    productRepository.findById("67bd62f974edd96bbd59874a")
}

val notFound = runBlocking {
    productRepository.findById("67bd62f974edd96bbd59874b")
}

// Logs: 

// Product(id=67bd62f974edd96bbd59874a, name=Product 1, description=Description 1, price=19.99, category=TOOLS, tags=[EXCLUSIVE, BESTSELLER])

// null</pre>



<p>As we can see, our test proves that <em>findById</em> not only works but also it simply returns null when nothing was found. No unexpected exceptions, etc. </p>



<h3 class="wp-block-heading" id="h-updating-products">Updating Products</h3>



<p>As the next step, let&#8217;s add the function responsible for updating products: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">suspend fun update(id: String, product: Product): Product? {
    val objectId = ObjectId(id)

    return productCollection.findOneAndReplace(
        filter = eq("_id", objectId),
        replacement = product,
        options = FindOneAndReplaceOptions().returnDocument(ReturnDocument.AFTER)
    )
}</pre>



<p>Again, this function is a suspended function, and again we use the same combination of the <em>eq</em> filter and <em>ObjectId</em> to find the item we are interested in. </p>



<p>The function that we use- <code>findOneAndReplace</code>&#8211; allows us to simply replace the existing document by passing a new version. Nevertheless, by default, this function returns the <strong>object before the update!</strong> And in my opinion, it makes more sense to return the updated versions in this case. And that&#8217;s why we specify the additional option. </p>



<p>As a note: if you would like to update just some fields, then the <code>findOneAndUpdate</code> may be a better choice. </p>



<p>With all of that done, let&#8217;s test our functionality: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">val updated = runBlocking {
    productRepository.update(
        id = "67bd62f974edd96bbd59874a",
        product = Product(
            name = "Updated Product 1",
            description = "Updated Description 1",
            price = 20.11,
            category = ProductCategory.FOOD,
            tags = listOf(ProductTag.BESTSELLER)
        )
    )
}

val notUpdated = runBlocking {
    productRepository.update(
        id = "67bd62f974edd96bbd59874b",
        product = Product(
            name = "Updated Product 2",
            description = "Updated Description 3",
            price = 20.11,
            category = ProductCategory.FOOD,
            tags = listOf(ProductTag.BESTSELLER)
        )
    )
}

// Logs: 

// Product(id=67bd62f974edd96bbd59874a, name=Updated Product 1, description=Updated Description 1, price=20.11, category=FOOD, tags=[BESTSELLER])

// null</pre>



<p>As we can see, everything works as expected. Our function returns the <strong>updated</strong> product. And moreover, it does not throw any exceptions when a product is not found! </p>



<h3 class="wp-block-heading" id="h-delete-products">Delete Products</h3>



<p>Nextly, let&#8217;s take a look at how we can remove products from our 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="">suspend fun deleteById(id: String): Boolean {
    val objectId = ObjectId(id)

    val deleteResult = productCollection.deleteOne(
        eq("_id", objectId)
    )

    return deleteResult.deletedCount == 1L
}</pre>



<p>At this point of our MongoDB coroutines tutorial, I think the code is quite descriptive. We use the same pattern we did previously to find by ID and we utilize the function from <code>DeleteResult</code> to get the count of deleted items. </p>



<p>Of course, given we have only one item with a particular <code>_id</code>, the function returns <em>true</em> only if the count is equal to one. </p>



<p>And again, a small note from my end if you would like to return the deleted product instead, then you can use the <code>findOneAndDelete</code> instead.</p>



<p>I will skip the testing part here, you must trust me 😀</p>



<h3 class="wp-block-heading" id="h-case-insensitive-search-in-mongodb">Case-insensitive Search In MongoDB</h3>



<p>As the last step, I will show you how to utilize the function that we already know (<code>find</code>) to perform a case-insensitive search in MongoDB: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun find(
    title: String,
): Flow&lt;Product> {
    return productCollection.find(
        regex(Product::name.name, title, "i")
    )
}</pre>



<p>As we can see, this time we make use of the <code>regex</code> function, that will add the&#8230; regex filter 😀 And thanks to the <strong>&#8220;i&#8221;</strong> option, the whole search will be case-insensitive.</p>



<p>Additionally, we make use of the <em>name</em> field reference. We could use a simple String value- &#8220;name&#8221;- but thanks to our approach we won&#8217;t need to remember to manually update the name in case of the update. </p>



<p>Lastly, I just wanted to mention here that this is one of the approaches to tackle this issue. According to the Mongo docs, we have also two more options: </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>1. Create a case-insensitive index with a collation strength of 1 or 2, and specify that your query uses the same collation.<br>2. Set the default collation strength of your collection to 1 or 2 when you create it, and do not specify a different collation in your queries and indexes.</p>
</blockquote>



<h3 class="wp-block-heading" id="h-adding-sorting-and-pagination-to-search-results">Adding Sorting and Pagination to Search Results</h3>



<p>Lastly, let&#8217;s make a small adjustment to add pagination and sorting to our logic.</p>



<p>To do so, let&#8217;s implement the <code>Order</code> Enum first:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">enum class Order {
    ASC, DESC
}</pre>



<p>And with that done, let&#8217;s get back to our repository: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fun find(
    title: String,
    sortBy: String,
    order: Order,
    limit: Int,
    skip: Int,
): Flow&lt;Product> {
    val sort = when (order) {
        Order.ASC -> ascending(sortBy)
        Order.DESC -> descending(sortBy)
    }

    return productCollection.find(
        regex(Product::name.name, title, "i")
    )
        .sort(sort)
        .limit(limit)
        .skip(skip)
}</pre>



<p>As we can see, our function allows us to pass 4 more arguments during the invocation: <em>sortBy</em>, <em>order</em>, <em>limit</em>, and <em>skip</em>.</p>



<p>So from now on, we can not only perform the search but also set the limit of results, ordering, as well as the order of items.</p>



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



<p>And that&#8217;s all for this tutorial on how to work with MongoDB and Kotlin coroutines in Ktor. </p>



<p>I hope you enjoyed it, and if you would like to learn more Ktor concepts in a fully hands-on manner, then check out my <a href="https://codersee.com/courses/ktor-server-pro/">course</a>. </p>



<p>Lastly, if you would like to get the whole codebase, then you can find it in <a href="https://github.com/codersee-blog/ktor-mongodb-coroutines" target="_blank" rel="noreferrer noopener">this GitHub repository</a>.</p>



<p></p>
<p>The post <a href="https://blog.codersee.com/mongodb-kotlin-coroutines-ktor/">MongoDB with Kotlin Coroutines in Ktor</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/mongodb-kotlin-coroutines-ktor/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 loading="lazy" 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="auto, (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>MongoDB Overview. Introduction To The Most Popular NoSQL DB</title>
		<link>https://blog.codersee.com/mongodb-overview-introduction-to-the-most-popular-nosql-db/</link>
					<comments>https://blog.codersee.com/mongodb-overview-introduction-to-the-most-popular-nosql-db/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 14 Feb 2023 06:00:48 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504225</guid>

					<description><![CDATA[<p>This MongoDB overview will be a great introduction to the most popular NoSQL database- from its benefits to real-life use cases.</p>
<p>The post <a href="https://blog.codersee.com/mongodb-overview-introduction-to-the-most-popular-nosql-db/">MongoDB Overview. Introduction To The Most Popular NoSQL DB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Hello friend and welcome to the <strong>MongoDB overview </strong>🙂 In this introduction, we will cover everything from its <strong>genesis, benefits, and drawbacks</strong> to<strong> real-life use cases</strong>.</p>
<p>Please keep in mind this is the 4th blog post in a series about <strong>the most popular databases</strong>, and you can find the previous articles right here:</p>
<ul>
<li><a href="https://blog.codersee.com/discover-postgresql-comprehensive-introduction/">Discover the Power of PostgreSQL: A Comprehensive Introduction</a></li>
<li><a href="https://blog.codersee.com/mysql-essentials-fast-paced-introduction/">MySQL Essentials: A Fast-Paced Introduction</a></li>
<li><a href="https://blog.codersee.com/mysql-essentials-fast-paced-introduction/">Redis Database Explained</a></li>
</ul>
<p>Please sit back, relax, and get ready to discover why MongoDB is taking the world by storm.</p>
<h2>Explainer Animation</h2>
<p>If you enjoy <strong>whiteboard animations</strong>, then right here you can find a video version of this article:</p>
<div style="text-align: center; width: 80%; margin-left: 10%;">
<p><a href="https://blog.codersee.com/mongodb-overview-introduction-to-the-most-popular-nosql-db/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FIPlERcyb25E%2Fhqdefault.jpg" alt="YouTube Video"></a></p>
</div>
<p>&nbsp;</p>
<p>If you find this content useful,<strong> please leave a subscription </strong> 😉</p>
<h2>MongoDB History</h2>
<p>As the first thing in our MongoDB overview, let&#8217;s take a second to learn more about its genesis.</p>
<p>MongoDB&#8217;s history starts in <strong>October 2007</strong> in a company called <strong>10gen</strong>. The founders were looking for a scalable solution, capable of dealing with a high volume in their internal project. (Un)Fortunately, at that time relational databases were not well-suited to handle the kind of semi-structured data they needed. As a result, they decided to build their own NoSQL database with the scalability and large amounts of unstructured data handling in mind.</p>
<p>Almost two years later, in <strong>February 2009</strong>, Mongo was released as an open-source project. Since then, it quickly gained popularity among developers and businesses who were looking for more flexible solutions. Of course, compared to relational databases.</p>
<p><strong>In 2013</strong>, 10gen changed its name to MongoDB, reflecting the growing popularity of the database.</p>
<p>Today, Mongo is widely used by organizations of all sizes, across various industries. Not to mention, we can choose not only between the Community and Enterprise editions but also a cloud-based database as a service called MongoDB Atlas.</p>
<h2>MongoDB Overview</h2>
<p>MongoDB is written in C++ and uses a binary form of JSON called <strong>BSON (Binary JSON)</strong> to store data. These documents can have fields that hold values of different types, such as:</p>
<ul>
<li>strings,</li>
<li>numbers,</li>
<li>arrays,</li>
<li>and even nested documents.</li>
</ul>
<p>Just like rows in relational databases are combined together in tables, Mongo <strong>documents</strong> are grouped in <strong>collections</strong>. Of course, with one, important difference: each document can have a different structure.</p>
<p><figure id="attachment_5504238" aria-describedby="caption-attachment-5504238" style="width: 751px" class="wp-caption aligncenter"><img loading="lazy" decoding="async" class="wp-image-5504238 size-full" src="http://blog.codersee.com/wp-content/uploads/2023/01/mongodb_collection_and_document_viewed_in_compass.png" alt="Image is a screenshot from MongoDB Compass and is an overview of Mongo documents stored in an collection. " width="751" height="575" srcset="https://blog.codersee.com/wp-content/uploads/2023/01/mongodb_collection_and_document_viewed_in_compass.png 751w, https://blog.codersee.com/wp-content/uploads/2023/01/mongodb_collection_and_document_viewed_in_compass-300x230.png 300w" sizes="auto, (max-width: 751px) 100vw, 751px" /><figcaption id="caption-attachment-5504238" class="wp-caption-text">Example collection</figcaption></figure></p>
<p>When it comes to <strong>scalability</strong>, MongoDB uses a sharding architecture to scale horizontally. Sharding, in simple words, allows a Mongo cluster to store more data than can fit on a single server by automatically partitioning the data across multiple machines. In such a situation, each shard contains a subset of the data and the query will be directed to the appropriate shard based on the query.</p>
<p>Of course, MongoDB also provides additional advanced features, like complex query language, indexing, a full-text search, and a replica set mechanism.</p>
<h2>Mongo Advantages</h2>
<p>So, with all of that covered, we can take a while to learn a couple of <strong>MongoDB advantages: </strong></p>
<ul>
<li>Firstly, <strong>flexibility</strong>. As I already mentioned in this MongoDB overview, documents within a collection do not need to have the same structure, or fields, unlike traditional relational databases. Mongo allows for a flexible schema and can be a great choice for some projects.</li>
<li>Moreover, <strong>document-oriented storage </strong>makes it relatively easy to work with MongoDB using popular programming languages that have built-in support for JSON.</li>
<li>Thirdly, <strong>great performance</strong>. Mongo is designed for high performance and can handle large amounts of data.</li>
<li>Additionally, <strong>scalability</strong> and <strong>high availability</strong>. MongoDB can be easily scaled and comes with built-in support for automatic failover and replication.</li>
<li>And finally- <strong>it&#8217;s easy to use</strong>. Mongo provides a simple command-line interface and it is easily integrated with a lot of programming languages and tools.</li>
</ul>
<h2>Disadvantages</h2>
<p>Of course, I must list a couple of disadvantages in this MongoDB introduction, as well:</p>
<ul>
<li>Firstly, Mongo <strong>uses a MongoDB Query Language (MQL)</strong>. And although it has the same syntax as documents (which makes it really intuitive), it&#8217;s nothing universal and we have to learn it in order to work with the database. This is not the case, for example when working with SQL.</li>
<li>Moreover, <strong>MongoDB does not support joins</strong>, which we know from relational databases. And although combining documents, or collections together is still doable, it might be a bit tricky task.</li>
<li>Additionally, documents cannot <strong>exceed 16MB</strong>.</li>
<li>And lastly- <strong>data redundancy</strong>. Due to the lack of joins, we oftentimes duplicate data across the documents, which may lead to harder maintenance.</li>
</ul>
<h2>Overview Of MongoDB Use Cases</h2>
<p>With all of that said, let&#8217;s figure out when Mongo can be a good choice:</p>
<ul>
<li>And as the first one- <strong>content management</strong>. It can be used to store and manage large amounts of structured and unstructured content, thanks to its flexibility.</li>
<li>Secondly, the <strong>Internet of Things</strong>. MongoDB is a popular choice for storing and processing large amounts of data generated by IoT devices. Mongo is not only great at dealing with large amounts of data but also can handle semi-structured and unstructured data sent by IoT devices.</li>
<li>Additionally-<strong> e-commerce and recommendation systems</strong>. A flexible schema allows for easy storage of various types of product data, like prices, descriptions, etc. Moreover, MongoDB support for complex queries and aggregations can help easily create search engines and recommendation systems</li>
<li>And as the last one, a bit more general- <strong>real-time applications</strong>. MongoDB&#8217;s low latency and high performance make it a great choice for any real-time application, like social media for instance.</li>
</ul>
<h2>Which Companies Use MongoDB?</h2>
<p>And as the last thing in our MongoDB overview, let&#8217;s figure out which companies are using it in their technology stack.</p>
<p><strong>eBay</strong> uses MongoDB to store information on items for sale and to power the search functionality. Additionally, Mongo is used to store data about users&#8217; browsing and buying history, which is then used for personalization purposes.</p>
<p>Another great example is <strong>Vodafone</strong>, which uses it for IoT, new 5G innovations, and cloud-native apps.</p>
<p>Moreover, <strong>Uber</strong> makes use of Mongo to store and manage data related to its operations, such as information on drivers, riders, and rides.</p>
<p>Of course, the list is much longer and contains over 35,000 customers, like Forbes, MetLife, <a href="https://be.elementor.com/visit/?bta=206789&amp;nci=5383">Elementor</a>, and <a href="https://go.fiverr.com/visit/?bta=631764&amp;brand=fiverrhybrid">Fiverr</a>.</p>
<h2>MongoDB Overview &#8211; Summary</h2>
<p>And that&#8217;s all for this <strong>MongoDB overview</strong>. I hope that you enjoyed this introduction and if that&#8217;s the case, then <strong>please let me know in the comments</strong> 🙂</p>
<p>Of course, if you&#8217;d like to expand your knowledge of MongoDB in practice, then check out my other resources:</p>
<ul>
<li><a href="https://codersee.com/courses/rest-api-with-micronaut-kotlin-and-mongodb-course/">REST API With Micronaut, Kotlin, and MongoDB Course</a></li>
<li><a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">A Guide To Ktor With MongoDB</a></li>
<li><a href="https://blog.codersee.com/spring-boot-mongodb-rest-api-crud-with-kotlin/">Spring Boot MongoDB REST API CRUD with Kotlin</a></li>
</ul>
<p>Thank you and see you in the next articles!</p>
<p>The post <a href="https://blog.codersee.com/mongodb-overview-introduction-to-the-most-popular-nosql-db/">MongoDB Overview. Introduction To The Most Popular NoSQL DB</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/mongodb-overview-introduction-to-the-most-popular-nosql-db/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>A Quick Intro To The 8 Most Popular Databases</title>
		<link>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/</link>
					<comments>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Wed, 14 Dec 2022 07:00:33 +0000</pubDate>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Elasticsearch]]></category>
		<category><![CDATA[MariaDB]]></category>
		<category><![CDATA[Microsoft SQL Server]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[SQLite]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=5504084</guid>

					<description><![CDATA[<p>Data is one of the most valuable assets of the XXI century and in this article, we will take a look at the 8 most popular databases of 2022.</p>
<p>The post <a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/">A Quick Intro To The 8 Most Popular Databases</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>It won&#8217;t be a surprise if I say that <strong>data is one of the most valuable assets of the XXI century</strong>. Many organizations and businesses rely on the most popular databases to store, manage and analyze their data to support their business needs.</p>
<p>In this article, we will take a look at the <strong>8 most popular databases</strong> and their key features, as well as common use cases.</p>
<p>So, no matter whether you are a database administrator, programmer, or simply would like to learn more about the different databases available, this article will provide you with all the necessary information to start with.</p>
<h2>Doodly Animation</h2>
<p>If you find this content useful, please leave a <strong>thumb up</strong>, or a <strong>sub</strong>. This way you can help me, so that I can help you 😉</p>
<p>&nbsp;</p>
<div style="text-align: center;">
<p><a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2Fu81dp3igZVU%2Fhqdefault.jpg" alt="YouTube Video"></a></p>
</div>
<h2>What Does &#8220;The Most Popular Databases&#8221; Mean?</h2>
<p>Well, before we start I wanna make sure that we are on the same page when referring to the &#8220;most popular&#8221;.</p>
<p>Depending on who wrote the article, this term can have various meanings. For some, it will be the frequency of Google searches. For others, it may be data provided by the <a href="https://db-engines.com" target="_blank" rel="noopener">https://db-engines.com</a> page.</p>
<p>And for the purpose of this blog post, I decided to use the <a href="https://survey.stackoverflow.co/2022/" target="_blank" rel="noopener">Stack Overflow Developer Survey 2022 results</a>, which present, as follows:</p>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" class="aligncenter wp-image-5504088 size-large" src="http://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-1024x480.png" alt="The image is a screenshot of StackOverflow Developer Survey 2022 presenting the most popular databases: MySQL, PostgreSQL, SQLite, MongoDB, Microsoft SQL Server, Redis, MariaDB and ElasticSearch" width="800" height="375" srcset="https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-1024x480.png 1024w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-300x141.png 300w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey-768x360.png 768w, https://blog.codersee.com/wp-content/uploads/2022/12/the_most_popular_databases_2022_so_developer_survey.png 1253w" sizes="auto, (max-width: 800px) 100vw, 800px" /></p>
<p>&nbsp;</p>
<p>In the next chapters, I will walk you through each of the 8 most popular databases shortly.</p>
<p>Please keep in mind that this article is a general overview and expect that more thorough continuations will be added in the upcoming weeks 🙂</p>
<h2>MySQL Database</h2>
<p>With that being said, let&#8217;s start with the winner- <strong>MySQL</strong>.</p>
<p>It&#8217;s an open-source relational database management system (aka RDBMS). It was created in 1994 by the Swedish company <em>MySQL AB</em> (which was later acquired by <em>Sun Microsystems</em> and then by <em>Oracle Corporation</em>). Originally, it was developed as a proprietary database system but later it was open-sourced under the GNU General Public License.<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></p>
<p>&nbsp;</p>
<h3>Advantages:</h3>
<ul>
<li><strong>It&#8217;s free and open-source,</strong> so it can be a great choice for smaller companies and organizations preferring to cut costs.</li>
<li>Moreover, MySQL <strong>is widely supported by web hosting providers,</strong> which means that it can be easily integrated with web-based applications.</li>
<li>Additionally, it has a <strong>low learning curve</strong> and a <strong>simple and intuitive user interface</strong>, so it is not only a great choice for beginners but also for less experienced teams.</li>
<li>Finally, <strong>a large and active community of users and developers </strong>means a lot of online resources and support available.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>First of all, <strong>it is not designed for applications that require real-time data processing or high availability</strong>. And technically- this is not a drawback, but rather something you should be aware of.</li>
<li>Whatsoever, MySQL is <strong>hard to scale</strong> and <strong>not intended for large amounts</strong> of data.</li>
<li>Lastly, <strong>it does not have built-in support for full-text search or geospatial data and some advanced features that are available in other database systems.</strong></li>
</ul>
<p>As I mentioned above, I will create a separate blog post about the king of the most popular databases. But for now, I can invite you to check out my other how-to guides with MySQL:</p>
<ul>
<li><a href="https://blog.codersee.com/deploy-kotlin-spring-boot-app-with-mysql-on-kubernetes/">Deploy Kotlin Spring Boot App with MySQL on Kubernetes</a></li>
<li><a href="https://blog.codersee.com/spring-boot-on-google-gke-with-cloud-sql-and-kotlin/">Spring Boot on Google GKE with Cloud SQL and Kotlin</a></li>
</ul>
<h2>PostgreSQL (aka Postgres)</h2>
<p>Nextly, let&#8217;s take a look at the second place with a 3% difference- <strong>PostgreSQL</strong> (also known as <strong>Postgres</strong>).</p>
<p>It was created in 1986 by a team of researchers at the University of California and is a <strong>powerful</strong> and <strong>open-source</strong> object-relational database management system. PostgreSQL is a highly popular and widely used DBMS, known for its flexibility, reliability, and performance.</p>
<h3>Advantages:</h3>
<ul>
<li>Just like MySQL, PostgreSQL is <strong>open-source</strong>, <strong>free to use, </strong>and<strong> has a large, active community.</strong></li>
<li>It is <strong>highly reliable</strong> and <strong>performs well even under heavy workloads</strong>. This makes it a good choice for applications that require high availability and scalability.</li>
<li>Moreover, Postgres comes with <strong>support for a lot of features and capabilities </strong>out of the box. For example triggers, stored procedures, and multi-row transactions.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Firstly, PostgreSQL <strong>may not be as widely supported</strong> or compatible with technologies, as some other database systems.</li>
<li>Secondly, it <strong>may not be as fast or scalable as other database management systems</strong>, such as NoSQL databases. And whereas it works well for large datasets, it is not the speed demon.</li>
<li>Whatsoever, Postgres is a pretty complex system, so <strong>it might be a bit difficult to learn</strong>.</li>
</ul>
<p>And just like previously, please expect new articles to come. But, again, if you would like to learn backend development with Kotlin and Postgres, then I can recommend:</p>
<ul>
<li><a href="https://codersee.com/courses/rest-api-with-spring-webflux-and-kotlin/">REST API With Spring WebFlux and Kotlin Course</a></li>
<li><a href="https://blog.codersee.com/tag/postgresql/">other tutorials on how to use it with Spring Boot, or Ktor</a></li>
</ul>
<h2>SQLite</h2>
<p>As the next step, let&#8217;s take a while to the no 3 in the most popular databases summary- <strong>SQLite</strong>.</p>
<p>SQLite is an embedded SQL database engine. The main difference between it and most of SQL databases is that it is self-contained and does not run a separate server process. To put it simply- it&#8217;s a library, which reads and writes directly to plain disk files.</p>
<p>It was created in 2000 by Dwayne Richard Hipp using a C programming language and since then, it become the most deployed database engine- used, among others of top web browsers.</p>
<h3>Advantages:</h3>
<ul>
<li>First of all, SQLite is a <strong>really lightweight</strong> database and it can be successfully used in low-memory environments. With all features enabled, its size can be as little as 750KiB.</li>
<li>Additionally, the database file format<strong> is portable</strong> and can be migrated between 32-bit ad 64-bit systems.</li>
<li>Moreover, SQLite is<strong> easy to learn</strong> and <strong>requires zero configuration</strong>, which makes it a great choice for beginners (and not only!)</li>
<li>Finally, it is <strong>really fast</strong> and can bring much better performance, than the filesystem.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Firstly- it&#8217;s <strong>not suitable for client/server applications</strong>. Although it can work over the network, the filesystem latency might be an issue.</li>
<li>Secondly (and lastly), SQLite <strong>is not a good choice for high-volume websites</strong> or environments demanding<strong> high concurrency</strong>. Whereas it does not have a limit for concurrent reads, only one writer can write at any instant in time.</li>
</ul>
<h2>MongoDB (aka Mongo)</h2>
<p>And the prize for 4th place in our most popular databases ranking goes to <strong>MongoDB</strong>.</p>
<p>MongoDB is an open-source document-oriented database. It was created in 2009 by MongoDB Inc., which makes it not only the first NoSQL representative in the ranking but also the youngest one (so far). In contrast to previous examples, MongoDB is an example of a NoSQL database, so instead of <em>tables</em> and <em>rows</em>, Mongo stores JSON-like <em>documents</em>, which are organized in <em>collections</em>.</p>
<h3>Advantages:</h3>
<ul>
<li>First of all, Mongo was built with environments demanding <strong>high performance</strong> and <strong>scalability </strong>in mind.</li>
<li>Secondly, it&#8217;s <strong>flexible</strong>. MongoDB does not require you to define a schema for your collections (SQL tables counterparts), which can help you reduce the time spent on schema changes. Moreover, it can be a great choice everywhere, where a schema is hard to define.</li>
<li>Finally, just like MySQL and Postgres, MongoDB is <strong>widely supported</strong> and really <strong>easy to learn</strong>.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>No <strong>out-of-the-box support for joins</strong>. Unlike relational databases, Mongo by design does not support joins between collections, so if you have to work with highly connected data sets, then it might not be the best choice.</li>
<li>Moreover, MongoDB documents <strong>cannot exceed 16MB.</strong></li>
<li>Lastly, the lack of joins oftentimes leads to <strong>data duplication</strong>, which may become troublesome to maintain in the future.</li>
</ul>
<p>If you would like to learn Mongo in practice, then check out my <a href="https://blog.codersee.com/tag/mongodb/">practical guides about MongoDB with Spring Boot, Ktor, and Micronaut</a>.</p>
<h2>Microsoft SQL Server</h2>
<p>Following, the 5th position on the most popular databases list is taken by <strong>Microsoft SQL Server</strong>.</p>
<p>It was developed by Microsoft and first released in 1989 and is another relational database in our ranking. Microsoft SQL Server lets us communicate using T-SQL (Transact-SQL), which is an extension of SQL bringing additional features, like functions, or local variables. Whatsoever, it comes with an entire range of products, which we can optionally configure together to make it a really powerful tool.</p>
<h3>Advantages:</h3>
<ul>
<li>Firstly, Microsoft SQL Server has really <strong>good support</strong> and <strong>documentation</strong>.</li>
<li>Additionally, it&#8217;s relatively <strong>easy to set up and use</strong>.</li>
<li>Whatsoever, SQL Server provides a range of <strong>security features</strong> to protect against unauthorized access to the data and <strong>ensure data integrity</strong>.</li>
<li>Lastly, a strong point is <strong>data recovery</strong> in case anything happens.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>First of all, <strong>pricing</strong>. And although there is a free option available, Enterprise or Standard editions are pretty expensive.</li>
<li>Secondly, <strong>compatibility</strong>. Prior to the SQL Server 2017, it could be run on Windows-based servers, but this is the first version, which can be deployed on Linux.</li>
<li>Lastly, it <strong>may require additional Microsoft software purchases</strong> to fully make use of it in your case.</li>
</ul>
<h2>Redis</h2>
<p>And the 6th most popular database of 2022 is <strong>Redis</strong>.</p>
<p>It was created by Salvatore Sanfilippo, also known as antirez, in 2009, which makes it a relatively young database in our summary.</p>
<p>Redis is an open-source, in-memory data structure store that is commonly used as a database, cache, and message broker. It is known for its high performance, flexibility, and scalability, making it a popular choice for a wide range of applications. Whatsoever, it is oftentimes used in conjunction with Microservices and containers, to support the needs of modern, distributed applications.</p>
<h3>Advantages:</h3>
<ul>
<li>As the first one- Redis is an <strong>in-memory data store</strong>, which results in much higher data processing, than disk-based databases.</li>
<li>Additionally, it&#8217;s <strong>highly scalable</strong> and <strong>can be easily expanded</strong> to support the growing needs of our application.</li>
<li>Lastly, Redis is an <strong>open-source</strong> project with a<strong> large community</strong> and a<strong> huge amount of online resources</strong> to use and learn from. Moreover, <strong>it is compatible</strong> and pretty<strong> easy to use with most of the popular frameworks</strong>.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>So the first advantage is the disadvantage, as well. The in-memory data store is actually faster, but we have to keep in mind that it is <strong>more expensive</strong>.</li>
<li>Moreover, it is a key-value store (like a dictionary), which means that the application(s) connecting to Redis <strong>have to be aware of the structure</strong>, which requires additional config.</li>
<li>Lastly, Redis is doing fine when dealing with key-based operations, but <strong>is not that efficient, when it comes to searching through the data set</strong>.</li>
</ul>
<p>If you would like to learn more about it in detail, then check out my more detailed article: <a href="https://blog.codersee.com/redis-database-explained/">Redis Database Explained</a></p>
<h2>MariaDB</h2>
<p>As the one before last in the most popular databases list, let&#8217;s see what exactly <strong>MariaDB</strong> is.</p>
<p>First of all, MariaDB is a fork of the MySQL DBMS, created by the original developers of MySQL in response to the acquisition of MySQL by Oracle Corporation. The acquisition raised concerns among many users and developers about the future direction of the MySQL project, which resulted in MariaDB being released in 2009.</p>
<p>It&#8217;s known for its performance, reliability, and ease of use, and is used by many organizations around the world. Moreover, it is designed to be backward-compatible with MySQL and can be used as a drop-in replacement for it in most cases.</p>
<h3>Advantages:</h3>
<ul>
<li>Firstly, MariaDB is known for its <strong>performance</strong> and <strong>reliability</strong>.</li>
<li>Additionally, it is an<strong> open-source</strong> project with a <strong>large</strong> and <strong>active community</strong> of users and developers. And I know that this argument repeats often in this article. But trust me- a wealth of resources, such as documentation and tutorials, available online comes with plenty of other perks.</li>
<li>Thirdly, as mentioned above <strong>it can be used as a MySQL drop-in replacement in most cases</strong>. This allows companies to replace the database without changing the other tools. However, please remember that this is not always the case and sometimes may require additional work.</li>
<li>And lastly, it is <strong>widely supported</strong> by various programming languages and frameworks.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>As the first one, <strong>some of the MySQL Enterprise Edition features are not available out of the box</strong>. And although this can be done through plugins, this is an additional, independent tool we have to incorporate.</li>
<li>Whatsoever, <strong>migrating from MySQL may result in additional adjustments and config</strong>.</li>
<li>And as the last thing- <strong>the support may be pretty expensive</strong>.</li>
</ul>
<h2>Elasticsearch</h2>
<p>And finally, we come to the last position in the top 8 most popular databases of 2022- <strong>Elasticsearch</strong>.</p>
<p>Elasticsearch is an open-source, full-text search and analytics engine created by Shay Banon in 2010.</p>
<p>It is designed to be scalable and flexible, making it a popular choice for building search and data analysis applications. Moreover, it&#8217;s based on the Lucene search engine library and provides a RESTful API for interacting with the data. Elasticsearch is known for its powerful search and analytics capabilities, and can be used to search, analyze, and visualize data from a variety of sources, including structured, unstructured, and time-series data.</p>
<h3>Advantages:</h3>
<ul>
<li>Elasticsearch is an <strong>open-source</strong> project, supported by a<strong> large and active community of users</strong>.</li>
<li>Similarly, it&#8217;s <strong>compatible with plenty of programming languages and frameworks</strong>, which helps in its implementation.</li>
<li>Lastly, it is a <strong>highly scalable search and analytics engine</strong>, which is intended for fast full-text searches. Thus, it can be a great solution for website searches.</li>
</ul>
<h3>Disadvantages:</h3>
<ul>
<li>Elasticsearch <strong>may not be as fast or efficient as some other search and analytics platforms</strong> in certain scenarios- for example, a large amount of data.</li>
<li>Moreover, it <strong>is more difficult to learn</strong> compared to similar tools. But this comes with being more powerful, as well.</li>
<li>Lastly, Elasticsearch <strong>should not be used as primary storage</strong>.</li>
</ul>
<h2>The Most Popular Databases Summary</h2>
<p>And that&#8217;s all for this article about the 8 most popular databases. As I already mentioned, this is the initial, bird&#8217;s-eye view post and you can expect more detailed articles to come.</p>
<p>Let me know your thoughts in the comments section below!</p>
<p>The post <a href="https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/">A Quick Intro To The 8 Most Popular Databases</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/a-quick-intro-to-8-most-popular-databases/feed/</wfw:commentRss>
			<slash:comments>2</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 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>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 loading="lazy" 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="auto, (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>A Guide To Ktor With MongoDB</title>
		<link>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/</link>
					<comments>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/#comments</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 15 Mar 2022 06:15:29 +0000</pubDate>
				<category><![CDATA[Ktor]]></category>
		<category><![CDATA[KMongo]]></category>
		<category><![CDATA[MongoDB]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=2002634</guid>

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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


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



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



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



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



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



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

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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

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

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

</pre>



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



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



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



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



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



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



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



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



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



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



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

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



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



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



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



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



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



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



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



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

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

}
</pre>



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



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



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



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



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



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



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



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



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



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



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



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



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

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

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



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



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



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



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

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

Response Status: 
201 Created

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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

  call.respond(peopleList)
}</pre>



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



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



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



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

Response Status: 200 OK

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



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



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



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



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



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



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



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



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



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



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



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

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



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



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



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



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

Response Status: 200 OK

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

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

Response Status: 404 Not Found

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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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



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

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

  call.respond(foundPeople)
}</pre>



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



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



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



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



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



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



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



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



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



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



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



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

  val updatedSuccessfully = service.updateById(id, person)

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



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



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



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



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

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

Response Status: 204 No Content

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

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

Response Status: 400 Bad Request

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



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



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



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



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



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



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



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



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



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



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

  val deletedSuccessfully = service.deleteById(id)

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



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



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



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



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

Response Status: 204 No Content

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

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



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



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



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



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



<p>If you would like to get the full source code for this project, please refer to this <a href="https://github.com/codersee-blog/ktor-mongodb-kmongo" target="_blank" rel="noopener"><strong>GitHub repository</strong></a>.</p>
<p>The post <a href="https://blog.codersee.com/a-guide-to-ktor-with-mongodb/">A Guide To Ktor With MongoDB</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/a-guide-to-ktor-with-mongodb/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>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-14 08:11:20 by W3 Total Cache
-->