<?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>Mongo Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/mongo/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:49:36 +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>Mongo 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>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-04-30 17:45:59 by W3 Total Cache
-->