<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abhishek Gupta</title>
    <description>The latest articles on DEV Community by Abhishek Gupta (@abhirockzz).</description>
    <link>https://dev.to/abhirockzz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F182847%2F60bbac9f-e6af-4220-bf17-dae44fffa2de.jpg</url>
      <title>DEV Community: Abhishek Gupta</title>
      <link>https://dev.to/abhirockzz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhirockzz"/>
    <language>en</language>
    <item>
      <title>A Chrome Extension That Talks to Your Database</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Mon, 23 Mar 2026 06:55:35 +0000</pubDate>
      <link>https://dev.to/abhirockzz/a-chrome-extension-that-talks-to-your-database-b7m</link>
      <guid>https://dev.to/abhirockzz/a-chrome-extension-that-talks-to-your-database-b7m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Cosmos DB Sidekick&lt;/strong&gt; is a Chrome extension built with the &lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;GitHub Copilot SDK&lt;/a&gt; and the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/sdk-nodejs" rel="noopener noreferrer"&gt;Azure Cosmos DB JavaScript SDK&lt;/a&gt;. The GitHub Copilot SDK lets you embed Copilot's AI capabilities directly into your apps — available for Go, Python, TypeScript, and .NET.&lt;/p&gt;

&lt;p&gt;It sits alongside the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator" rel="noopener noreferrer"&gt;Cosmos DB vNext emulator&lt;/a&gt; Data Explorer and lets you ask questions in plain English. It writes the queries, runs them, and shows results. No copy-pasting SQL, no switching between tabs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can find the code on &lt;a href="https://github.com/abhirockzz/cosmos-db-sidekick" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Here is a demo of the app in action&lt;/strong&gt;:&lt;/p&gt;



&lt;h2&gt;
  
  
  What can you actually do with it?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use natural language to query data.&lt;/strong&gt; In response to something like &lt;em&gt;"Find all orders over $100 from the last month"&lt;/em&gt; — the extension figures out the schema, generates a SQL query, runs it against your emulator, and streams back the results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write data too.&lt;/strong&gt; Need test data? Ask it to &lt;em&gt;"Add 10 test users to the users container"&lt;/em&gt; and it generates realistic documents with proper &lt;code&gt;id&lt;/code&gt; fields and partition keys, then upserts them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It knows what you're looking at.&lt;/strong&gt; When the Cosmos DB Data Explorer is open, the extension auto-detects which database and container you're browsing. A context bar shows something like &lt;code&gt;📂 ordersDb › customers&lt;/code&gt;, and your questions automatically target that data. Switch to a different container in the Data Explorer — the context follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u8unptf4e6oip4zaw10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9u8unptf4e6oip4zaw10.png" alt="Context-aware integration" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conversations persist.&lt;/strong&gt; You can have multi-turn conversations, switch between sessions, and pick up where you left off. Close the side panel, reopen it — your chat history is still there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftr28t2rt0aglsulbv2bk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftr28t2rt0aglsulbv2bk.png" alt="Conversation management" width="800" height="932"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Copilot SDK makes this work
&lt;/h2&gt;

&lt;p&gt;Under the hood, the extension is powered by the &lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;GitHub Copilot SDK&lt;/a&gt;. The architecture is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────┐
│  Chrome Extension   │
│    (side panel)     │
└────────┬────────────┘
         │ HTTP + SSE
┌────────▼────────────┐       ┌──────────────────────┐
│   Node.js Sidecar   │──────▶│  GitHub Copilot SDK  │
│                     │◀──────│  (LLM + tool calls)  │
└────────┬────────────┘       └──────────────────────┘
         │ Cosmos DB JS SDK
┌────────▼────────────┐
│  Cosmos DB vNext    │
│     Emulator        │
└─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sidecar is a lightweight Node.js server that acts as the bridge. It creates a &lt;code&gt;CopilotClient&lt;/code&gt;, manages chat sessions, and, (most importantly) defines &lt;strong&gt;tools&lt;/strong&gt; that the LLM can call autonomously. Tools are the key to the agentic behavior — they let the model interact with the external world (in this case, your Cosmos DB instance) in a structured way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_databases&lt;/code&gt; — discover what databases exist&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_containers&lt;/code&gt; — see containers and partition keys in a database
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sample_documents&lt;/code&gt; — grab sample docs to understand the schema&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run_query&lt;/code&gt; — execute a read-only SQL query&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upsert_items&lt;/code&gt; — insert or replace documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, what happens when you ask a question like &lt;em&gt;"What does the schema look like?"&lt;/em&gt;. You didn't tell it which database to query. You didn't tell it to sample documents. But the LLM chains the tools together on its own — lists databases, picks the one from your context, lists containers, samples documents, then describes what it found. That's the agentic part. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Same applies to query generation and execution - you define the tools, the SDK handles the orchestration, and the model decides the sequence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's what a tool definition looks like (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run_query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execute a read-only SQL query against a Cosmos DB container.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The database name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The container name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The SQL query to execute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK takes care of the rest — passing tool definitions to the model, routing tool calls to your handlers, feeding results back into the conversation, and streaming the final response.&lt;/p&gt;

&lt;p&gt;Streaming is built into the session. The sidecar subscribes to session events and forwards them over SSE to the Chrome extension, so responses appear token-by-token in the chat panel. Creating a streaming chat session is a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gpt-4.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;systemMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;append&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getTools&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;streaming&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onPermissionRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;approveAll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Session persistence comes for free too. The SDK's &lt;code&gt;listSessions()&lt;/code&gt; and &lt;code&gt;resumeSession()&lt;/code&gt; let the extension show chat history and restore previous conversations — the sidecar doesn't need its own storage layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context-aware — no configuration needed
&lt;/h2&gt;

&lt;p&gt;The extension watches which database and container you have open in the Data Explorer and automatically uses that as context. Ask &lt;em&gt;"show me the top 10 documents"&lt;/em&gt; and it knows exactly where to look — no need to specify the target every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;The whole thing runs locally — Chrome extension + a Node.js sidecar + the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator" rel="noopener noreferrer"&gt;Cosmos DB vNext emulator&lt;/a&gt; in Docker. You'll need &lt;a href="https://docs.github.com/en/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; and the &lt;a href="https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli" rel="noopener noreferrer"&gt;Copilot CLI&lt;/a&gt; set up for auth.&lt;/p&gt;

&lt;p&gt;Follow the &lt;a href="https://github.com/abhirockzz/cosmos-db-sidekick" rel="noopener noreferrer"&gt;steps in the GitHub repo&lt;/a&gt; to get it running, and start chatting with your database in natural language. It's a fun demo of how the GitHub Copilot SDK can turn a simple extension into an intelligent agent that understands your data and helps you interact with it seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger picture
&lt;/h2&gt;

&lt;p&gt;Cosmos DB Sidekick is one specific use case, but the pattern behind it is general. Define a few tools, hand them to the Copilot SDK, and you get an agent that can reason about when and how to use them — chaining calls, handling errors, and streaming results back to the user. The SDK handles sessions, tool orchestration, and model access; you just bring the domain logic.&lt;/p&gt;

&lt;p&gt;That same pattern works for any data source, any API, any workflow. A CLI that talks to your CI/CD pipeline. A Slack bot that queries your observability stack. A dashboard that lets non-technical teammates explore production metrics in plain English. The building blocks are the same: tools, a system prompt, and a session.&lt;/p&gt;

&lt;p&gt;If you're looking for a starting point, the &lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;Copilot SDK&lt;/a&gt; has samples for Go, Python, TypeScript, and .NET. Pick your stack and start building.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>database</category>
      <category>agents</category>
    </item>
    <item>
      <title>DocumentDB on Kubernetes: Resilient, Highly Available Databases with Automatic Failover</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Tue, 03 Mar 2026 08:06:34 +0000</pubDate>
      <link>https://dev.to/abhirockzz/documentdb-on-kubernetes-resilient-highly-available-databases-with-automatic-failover-ak7</link>
      <guid>https://dev.to/abhirockzz/documentdb-on-kubernetes-resilient-highly-available-databases-with-automatic-failover-ak7</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/documentdb/documentdb" rel="noopener noreferrer"&gt;DocumentDB&lt;/a&gt; is an open-source MongoDB-compatible database built on PostgreSQL that provides a familiar interface while leveraging PostgreSQL's reliability and extensibility. The &lt;a href="https://github.com/documentdb/documentdb-kubernetes-operator" rel="noopener noreferrer"&gt;DocumentDB Kubernetes Operator&lt;/a&gt; brings this database to Kubernetes environments by extending the platform with custom resources. The operator manages DocumentDB clusters declaratively, handling deployment, scaling, upgrades, and high availability scenarios automatically.&lt;/p&gt;

&lt;p&gt;The DocumentDB Kubernetes Operator provides multiple levels of high availability, each addressing a different failure domain. &lt;strong&gt;Local HA&lt;/strong&gt; deploys multiple database instances within a single Kubernetes cluster with automatic failover in seconds, protecting against pod and node failures. For further resilience, you can configure &lt;strong&gt;availability zone spreading&lt;/strong&gt; so that replicas land in different AZs, allowing the cluster to survive a full zone outage without manual intervention. Beyond a single cluster, the operator supports &lt;strong&gt;multi-region HA&lt;/strong&gt; across Azure regions (using KubeFleet) and &lt;strong&gt;multi-cloud HA&lt;/strong&gt; across providers like Azure, AWS, and GCP (using Istio). Both use physical WAL replication with manual failover via &lt;code&gt;kubectl documentdb promote&lt;/code&gt;. These levels are composable: a production deployment can combine all of them.&lt;/p&gt;

&lt;p&gt;This post focuses on local HA, the foundational layer, and walks through automatic failover in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Highly Available DocumentDB deployment on Kubernetes
&lt;/h2&gt;

&lt;p&gt;In single-instance database deployments, any failure (such as a pod crash, node issue, or planned upgrade) may result in downtime. Local high availability (HA) solves this problem by deploying multiple database instances within a single Kubernetes cluster. The operator creates one primary instance that handles all client operations, along with multiple replica instances that are continuously replicated via asynchronous WAL streaming. When the primary fails, a replica is automatically promoted to become the new primary, ensuring your application experiences minimal disruption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5826d4d46fq8rs5m0i4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq5826d4d46fq8rs5m0i4.png" alt="DocumentDB HA design" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Local HA is ideal when you need resilience within a single region without the complexity of multi-region deployments. It's a cost-effective solution for development and staging environments where you want to validate failover behavior without cloud distribution costs, as well as for production workloads that require automatic recovery from infrastructure failures. For cross-region disaster recovery scenarios, the operator also supports multi-cluster replication features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;DocumentDB's local HA leverages CloudNativePG (CNPG) as its underlying PostgreSQL foundation. CNPG handles WAL-based streaming replication and automatic failover orchestration, while DocumentDB adds the MongoDB-compatible protocol layer on top.&lt;/p&gt;

&lt;p&gt;Here are the key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CNPG Cluster&lt;/strong&gt;: Manages PostgreSQL replication (1 primary + N replicas) with WAL-based streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DocumentDB Gateway&lt;/strong&gt;: A sidecar container injected into each PostgreSQL pod that translates MongoDB wire protocol to PostgreSQL DocumentDB extension calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes Services&lt;/strong&gt;: Layered service architecture for different access patterns:

&lt;ul&gt;
&lt;li&gt;Internal PostgreSQL services (port 5432): &lt;code&gt;&amp;lt;cluster&amp;gt;-rw&lt;/code&gt; (primary), &lt;code&gt;&amp;lt;cluster&amp;gt;-ro&lt;/code&gt; (replicas only), &lt;code&gt;&amp;lt;cluster&amp;gt;-r&lt;/code&gt; (all instances — primary and replicas) - used for internal operations, metrics, and backups&lt;/li&gt;
&lt;li&gt;External Gateway service (port 10260): Routes MongoDB client traffic to the current primary by tracking the &lt;code&gt;cnpg.io/instanceRole: primary&lt;/code&gt; label&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;When the primary fails, CNPG automatically detects the failure and promotes the most advanced replica to primary, updating the pod labels. The Kubernetes service automatically follows the new primary, keeping the external IP stable – no manual DNS changes required. The operator currently supports a &lt;strong&gt;maximum of 3 instances&lt;/strong&gt; (&lt;code&gt;instancesPerNode: 3&lt;/code&gt;), providing 1 primary + 2 replicas for optimal balance between availability, performance, and operational flexibility.&lt;/p&gt;

&lt;p&gt;Let's see how this works in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Test Environment
&lt;/h2&gt;

&lt;p&gt;You need the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;minikube&lt;/code&gt;, &lt;code&gt;kubectl&lt;/code&gt;, and &lt;code&gt;helm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Python (for the test client application)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start your &lt;code&gt;minikube&lt;/code&gt; cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also make sure to clone the GitHub repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/abhirockzz/documentdb-local-ha-tutorial.git
&lt;span class="nb"&gt;cd &lt;/span&gt;documentdb-local-ha-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install DocumentDB Operator
&lt;/h3&gt;

&lt;p&gt;First, install &lt;code&gt;cert-manager&lt;/code&gt; (required dependency):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add jetstack https://charts.jetstack.io
helm repo update

helm &lt;span class="nb"&gt;install &lt;/span&gt;cert-manager jetstack/cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;installCRDs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the DocumentDB operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add documentdb https://documentdb.github.io/documentdb-kubernetes-operator

helm &lt;span class="nb"&gt;install &lt;/span&gt;documentdb-operator documentdb/documentdb-operator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt; documentdb-operator &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the operator is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get deployment &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;To check the operator version, run &lt;code&gt;kubectl get pods -n documentdb-operator -o jsonpath='{.items[*].spec.containers[*].image}' &amp;amp;&amp;amp; echo&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deploy Local HA Cluster
&lt;/h3&gt;

&lt;p&gt;In a separate terminal, start the &lt;code&gt;minikube tunnel&lt;/code&gt;. This creates a network route that enables &lt;code&gt;LoadBalancer&lt;/code&gt; services to receive external IPs in your local &lt;code&gt;minikube&lt;/code&gt; environment. The tunnel will assign an IP address to the &lt;code&gt;DocumentDB&lt;/code&gt; service and route traffic from your host machine to the cluster, allowing the Python test client to connect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube tunnel

&lt;span class="c"&gt;# output:&lt;/span&gt;
✅  Tunnel successfully started

📌  NOTE: Please &lt;span class="k"&gt;do &lt;/span&gt;not close this terminal as this process must stay alive &lt;span class="k"&gt;for &lt;/span&gt;the tunnel to be accessible ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/abhirockzz/documentdb-local-ha-tutorial/blob/main/local_ha.yaml" rel="noopener noreferrer"&gt;&lt;code&gt;local_ha.yaml&lt;/code&gt;&lt;/a&gt; file contains the complete configuration including namespace, credentials secret, and the DocumentDB resource with &lt;code&gt;instancesPerNode: 3&lt;/code&gt; to create a 3-instance HA cluster.&lt;/p&gt;

&lt;p&gt;Deploy the cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; local_ha.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor the pod status. Wait for all pods to be running (this may take 1-2 minutes). In the meantime, you should see output similar to this, and eventually all 3 pods will reach &lt;code&gt;Running&lt;/code&gt; status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-w&lt;/span&gt;

&lt;span class="c"&gt;# output:&lt;/span&gt;

NAME                                 READY   STATUS            RESTARTS   AGE
documentdb-local-ha-1-initdb-ffrjf   0/1     PodInitializing   0          3s
documentdb-local-ha-1-initdb-ffrjf   1/1     Running           0          24s
documentdb-local-ha-1-initdb-ffrjf   0/1     Completed         0          25s
documentdb-local-ha-1-initdb-ffrjf   0/1     Completed         0          27s
documentdb-local-ha-1-initdb-ffrjf   0/1     Completed         0          27s
documentdb-local-ha-1                0/2     Pending           0          0s
documentdb-local-ha-1                0/2     Pending           0          0s
//....
documentdb-local-ha-3                2/2     Running           0          11s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to the &lt;code&gt;minikube tunnel&lt;/code&gt; terminal, and verify the tunnel is now active for the DocumentDB service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Starting tunnel &lt;span class="k"&gt;for &lt;/span&gt;service documentdb-service-documentdb-local-ha.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify the external IP assigned to the service (should be &lt;code&gt;127.0.0.1&lt;/code&gt; in this case):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns

&lt;span class="c"&gt;# output:&lt;/span&gt;
NAME                                     TYPE           CLUSTER-IP       EXTERNAL-IP   PORT&lt;span class="o"&gt;(&lt;/span&gt;S&lt;span class="o"&gt;)&lt;/span&gt;           AGE
documentdb-local-ha-r                    ClusterIP      10.108.176.232   &amp;lt;none&amp;gt;        5432/TCP          4m47s
documentdb-local-ha-ro                   ClusterIP      10.111.154.246   &amp;lt;none&amp;gt;        5432/TCP          4m47s
documentdb-local-ha-rw                   ClusterIP      10.101.235.95    &amp;lt;none&amp;gt;        5432/TCP          4m47s
documentdb-service-documentdb-local-ha   LoadBalancer   10.97.24.62      127.0.0.1     10260:31829/TCP   4m57s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok now we are ready to test failover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Failover
&lt;/h2&gt;

&lt;p&gt;The test uses a &lt;a href="https://github.com/abhirockzz/documentdb-local-ha-tutorial/blob/main/failover_test_read_write.py" rel="noopener noreferrer"&gt;Python client application&lt;/a&gt; that continuously performs write and read operations against the DocumentDB cluster. This includes retry logic with exponential backoff and tracks metrics like operation counts, failures, and downtime. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that the client uses &lt;code&gt;retryWrites=True&lt;/code&gt;, which allows the MongoDB driver to automatically retry failed writes on the new primary — in very fast failovers, you may see zero reported failures as the driver absorbs the disruption transparently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Start the client application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python failover_test_read_write.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let it run for ~15 seconds to establish a baseline. You'll see continuous write and read operations succeeding.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//....
[CLIENT][10:29:28.051] ✓ Connected to DocumentDB
[CLIENT][10:29:28.138] ✓ W#1 (44ms) | R#1 (43ms) | Avg W:44ms R:43ms | Docs: 1
[CLIENT][10:29:28.688] ✓ W#2 (2ms) | R#2 (43ms) | Avg W:23ms R:43ms | Docs: 2
[CLIENT][10:29:29.234] ✓ W#3 (2ms) | R#3 (43ms) | Avg W:16ms R:43ms | Docs: 3
[CLIENT][10:29:29.783] ✓ W#4 (3ms) | R#4 (43ms) | Avg W:13ms R:43ms | Docs: 4
[CLIENT][10:29:30.327] ✓ W#5 (2ms) | R#5 (42ms) | Avg W:11ms R:43ms | Docs: 5
//.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trigger Failover
&lt;/h3&gt;

&lt;p&gt;In a new terminal, identify the current primary (the primary/replica roles may vary in your deployment):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-L&lt;/span&gt; cnpg.io/instanceRole

&lt;span class="c"&gt;# output:&lt;/span&gt;
NAME                    READY   STATUS    RESTARTS   AGE   INSTANCEROLE
documentdb-local-ha-1   2/2     Running   0          14m   primary
documentdb-local-ha-2   2/2     Running   0          14m   replica
documentdb-local-ha-3   2/2     Running   0          14m   replica
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note which pod shows &lt;code&gt;primary&lt;/code&gt; role, then delete it to simulate a failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete pod &amp;lt;primary-pod-name&amp;gt; &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, in case your primary pod is &lt;code&gt;documentdb-local-ha-1&lt;/code&gt;, you would run: &lt;code&gt;kubectl delete pod documentdb-local-ha-1 -n documentdb-preview-ns&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatic Recovery
&lt;/h3&gt;

&lt;p&gt;Watch the client terminal. You should see logs similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[CLIENT][10:32:32.452] ✗ FAILOVER EVENT DETECTED
[CLIENT][10:32:32.452] ℹ   Error: the database system is shutting down, full error: {'ok': 0.0, 'code': 50463173, 'codeName': 'Error', 'errmsg': 'the database system is shutting down'}
[CLIENT][10:32:32.452] ℹ   Last successful write: #178
[CLIENT][10:32:32.452] ℹ ================================================================================
[CLIENT][10:32:32.500] ✗ ⚠ Write FAILED | ⚠ Read FAILED | Downtime: 0.0s | Failed W: 1 R: 1
[CLIENT][10:32:32.551] ↻ Attempting reconnection (backoff: 1.0s)...
[CLIENT][10:32:32.703] ✗ Connection failed: error connecting to server: Connection refused (os error 111), full error: {'ok': 0.0, 'code': 1, 'codeName': 'Internal Error', 'errmsg': 'error connecting to server: Connection refused (os error 111)'}
[CLIENT][10:32:33.705] ↻ Attempting reconnection (backoff: 2.0s)...
[CLIENT][10:32:33.947] ✓ Connected to DocumentDB
[CLIENT][10:32:33.947] ✓ Reconnection successful!
[CLIENT][10:32:33.998] ℹ ================================================================================
[CLIENT][10:32:33.998] ↻ RECOVERY COMPLETE
//.....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the read and write operations should resume successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[CLIENT][10:32:34.044] ✓ W#179 (51ms) | R#179 (46ms) | Avg W:8ms R:39ms | Docs: 284
[CLIENT][10:32:34.593] ✓ W#180 (4ms) | R#180 (42ms) | Avg W:8ms R:39ms | Docs: 285
[CLIENT][10:32:35.143] ✓ W#181 (5ms) | R#181 (44ms) | Avg W:9ms R:39ms | Docs: 286
[CLIENT][10:32:35.693] ✓ W#182 (3ms) | R#182 (43ms) | Avg W:9ms R:39ms | Docs: 287
[CLIENT][10:32:36.238] ✓ W#183 (2ms) | R#183 (42ms) | Avg W:8ms R:39ms | Docs: 288
[CLIENT][10:32:36.787] ✓ W#184 (3ms) | R#184 (43ms) | Avg W:9ms R:39ms | Docs: 289
[CLIENT][10:32:37.333] ✓ W#185 (3ms) | R#185 (42ms) | Avg W:8ms R:39ms | Docs: 290
//....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a closer look at what happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  Behind the scenes
&lt;/h3&gt;

&lt;p&gt;These are the key events during failover:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Failure detection&lt;/strong&gt;: Operations start failing with connection errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FAILOVER EVENT DETECTED&lt;/strong&gt;: The client recognizes the disruption&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection attempts&lt;/strong&gt;: Automatic retry with exponential backoff&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RECOVERY COMPLETE&lt;/strong&gt;: Service automatically resumes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6b7573qdv92a4bmusmt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6b7573qdv92a4bmusmt.png" alt=" " width="800" height="917"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behind the scenes, CNPG automatically detects the primary pod termination, promotes a healthy replica to primary, and updates the &lt;code&gt;cnpg.io/instanceRole: primary&lt;/code&gt; label on the new primary pod. The Kubernetes service automatically routes traffic to the new primary (the external IP remains unchanged).&lt;/p&gt;

&lt;p&gt;The key to failover is the service's label selector mechanism. Since the service tracks pods with &lt;code&gt;cnpg.io/instanceRole: primary&lt;/code&gt;, when CNPG updates this label during promotion, the service endpoint automatically switches to the new primary without any DNS changes or client reconfiguration.&lt;/p&gt;

&lt;p&gt;Verify the new primary – you should see a different pod as the new primary (it may vary in your deployment). In this example, &lt;code&gt;documentdb-local-ha-3&lt;/code&gt; has become the new primary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-L&lt;/span&gt; cnpg.io/instanceRole

&lt;span class="c"&gt;# output:&lt;/span&gt;
NAME                    READY   STATUS    RESTARTS   AGE     INSTANCEROLE
documentdb-local-ha-1   2/2     Running   0          2m17s   replica
documentdb-local-ha-2   2/2     Running   0          18m     replica
documentdb-local-ha-3   2/2     Running   0          18m     primary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify manually
&lt;/h3&gt;

&lt;p&gt;You can also connect directly using &lt;code&gt;mongosh&lt;/code&gt; to verify. First, get the connection string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get documentdb documentdb-local-ha &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns

&lt;span class="c"&gt;# output:&lt;/span&gt;

NAME                  STATUS                     CONNECTION STRING
documentdb-local-ha   Cluster &lt;span class="k"&gt;in &lt;/span&gt;healthy state   mongodb://&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret documentdb-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.username}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;:&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret documentdb-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.password}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;@127.0.0.1:10260/?directConnection&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&amp;amp;authMechanism&lt;span class="o"&gt;=&lt;/span&gt;SCRAM-SHA-256&amp;amp;tls&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&amp;amp;tlsAllowInvalidCertificates&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&amp;amp;replicaSet&lt;span class="o"&gt;=&lt;/span&gt;rs0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the connection string to connect with &lt;code&gt;mongosh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mongosh &lt;span class="s2"&gt;"mongodb://&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret documentdb-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.username}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret documentdb-credentials &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.password}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;@127.0.0.1:10260/?directConnection=true&amp;amp;authMechanism=SCRAM-SHA-256&amp;amp;tls=true&amp;amp;tlsAllowInvalidCertificates=true&amp;amp;replicaSet=rs0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once connected, you can connect to the &lt;code&gt;testdb&lt;/code&gt; database and verify the documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rs0&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;direct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;use&lt;/span&gt; &lt;span class="nx"&gt;testdb&lt;/span&gt;
&lt;span class="nx"&gt;switched&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="nx"&gt;testdb&lt;/span&gt;
&lt;span class="nx"&gt;rs0&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;direct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;testdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCollectionNames&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failover_test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;rs0&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;direct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;testdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;failover_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countDocuments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;408&lt;/span&gt;
&lt;span class="nx"&gt;rs0&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;direct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mongos&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;testdb&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bonus exercise
&lt;/h3&gt;

&lt;p&gt;Try connecting to each cluster node directly. You can &lt;code&gt;kubectl port-forward&lt;/code&gt; to each pod. For example, to connect to &lt;code&gt;documentdb-local-ha-1&lt;/code&gt; over port &lt;code&gt;27017&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns documentdb-local-ha-1 27017:10260
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can connect with &lt;code&gt;mongosh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mongosh &lt;span class="s2"&gt;"mongodb://k8s_secret_user:K8sSecret100@localhost:27017/?directConnection=true&amp;amp;authMechanism=SCRAM-SHA-256&amp;amp;tls=true&amp;amp;tlsAllowInvalidCertificates=true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Experiment with different commands and observe the behavior.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also explore advanced scenarios like &lt;a href="https://github.com/documentdb/documentdb-kubernetes-operator/tree/main/documentdb-playground/aks-fleet-deployment" rel="noopener noreferrer"&gt;multi-region&lt;/a&gt; or &lt;a href="https://github.com/documentdb/documentdb-kubernetes-operator/tree/main/documentdb-playground/multi-cloud-deployment" rel="noopener noreferrer"&gt;multi-cloud&lt;/a&gt; deployments&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cleanup
&lt;/h2&gt;

&lt;p&gt;To tear down the environment when you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete namespace documentdb-preview-ns
helm uninstall documentdb-operator &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-operator
kubectl delete namespace documentdb-operator
minikube stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The DocumentDB Kubernetes Operator provides local high availability with automatic failover capabilities. You have seen how the operator handles primary failures with minimal manual intervention, making it easier to build resilient database deployments on Kubernetes. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This tutorial demonstrates basic failover recovery using a small dataset in a single-node cluster. In this case, the client-observed recovery time was approximately 1-3 seconds. Since CNPG uses asynchronous replication by default, note that transactions committed on the old primary but not yet replicated to standbys could be lost during an unplanned failover. Make sure to consider factors specific to your deployments for production or with larger datasets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Go ahead, try it out in your own environment, and let us know your feedback!&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://documentdb.github.io/documentdb-kubernetes-operator/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for the latest feature updates. If you run into issues or have questions, reach out on &lt;a href="https://discordapp.com/channels/1374170121219866635/1435045191156236458" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; or raise an issue on &lt;a href="https://github.com/documentdb/documentdb-kubernetes-operator/issues" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Contributions are welcome, whether it's code, documentation improvements, or simply sharing your experience.&lt;/p&gt;

&lt;p&gt;Happy building!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>postgres</category>
      <category>python</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Build a Time Tracking App with GitHub Copilot SDK</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Mon, 02 Mar 2026 06:09:41 +0000</pubDate>
      <link>https://dev.to/abhirockzz/build-a-time-tracking-app-with-github-copilot-sdk-22b6</link>
      <guid>https://dev.to/abhirockzz/build-a-time-tracking-app-with-github-copilot-sdk-22b6</guid>
      <description>&lt;p&gt;The &lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;GitHub Copilot SDK&lt;/a&gt; lets you embed Copilot's agentic workflows directly into your apps, and it's available for Python, TypeScript, Go, and .NET.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TimeTrack&lt;/strong&gt; is a desktop app built with the GitHub Copilot SDK (TypeScript) and &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/introduction" rel="noopener noreferrer"&gt;Azure Cosmos DB&lt;/a&gt;. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⏱️ Track time with a start/stop timer, projects, and tags&lt;/li&gt;
&lt;li&gt;🗣️ Ask questions about your time data in plain English — Copilot generates and runs Cosmos DB SQL queries behind the scenes&lt;/li&gt;
&lt;li&gt;📊 View reports and charts across multiple users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Here is a demo of the app in action&lt;/strong&gt;:&lt;/p&gt;



&lt;p&gt;If you want to try it yourself, the code is on &lt;a href="https://github.com/abhirockzz/cosmosdb-copilot-sdk-time-tracker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Works with Azure Cosmos DB or the vNext emulator — clone, configure, and &lt;code&gt;npm start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For local development, you don't need an Azure account. The &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Cosmos DB vNext emulator&lt;/a&gt; runs as a Docker container, so you can seed sample data and start querying right away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;What makes this interesting is the SDK's tool calling. You define a tool using &lt;code&gt;defineTool&lt;/code&gt; with a schema (powered by Zod), and Copilot decides when to call it. Here's what the core of it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryTool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;query_time_data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execute a read-only Cosmos DB SQL query against time entries&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cosmos DB SQL query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// query is scoped to the user's partition key&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;queryTool&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;systemMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a user asks &lt;em&gt;"what was my most productive day this week?"&lt;/em&gt;, the model generates a SQL query, executes it against Cosmos DB (scoped to the user's partition key), and returns a conversational answer. If the generated SQL hits a Cosmos DB error — say, an unsupported &lt;code&gt;ORDER BY&lt;/code&gt; on an aggregate alias — the error is returned to the model as a tool result. The model reads it, adjusts the query, and retries. This usually succeeds within 1–2 attempts, with no user intervention.&lt;/p&gt;

&lt;p&gt;Here's the overall flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------+     +----------------+     +-------------+
|  Electron UI | --&amp;gt; |  Copilot SDK   | --&amp;gt; | Copilot CLI |
|  (renderer)  |     |  (main process)|     |  (sidecar)  |
+--------------+     +----------------+     +------+------+
       ^                                           |
       |                                    tool call: query_time_data
       |                                           |
       |                                    +------v------+
       |                                    |  Cosmos DB  |
       |                                    +------+------+
       |                                           |
       |                                     result or error
       |                                           |
       |                                    +------v------+
       |                                    |    Model    |
       |                                    | (interpret) |
       |                                    +------+------+
       |                                      |         |
       |                                success?     SQL error?
       |                                   |          retry with
       |                                   |        corrected SQL
       |                                   |             |
       +---  AI response  &amp;lt;---------------+     (back to Cosmos DB)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the AI writes arbitrary SQL rather than picking from canned reports, the range of questions is wide. A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Do I tend to work longer hours early in the week or late in the week?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;How does my Monday workload compare to Friday?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Compare my total hours this week vs last week&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Rank my projects by total hours and show the percentage each represents&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Summarize my last two weeks in 3 bullet points&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw68kk9he0c8808x1pqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkw68kk9he0c8808x1pqr.png" alt="Report view and AI-generated query results" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app uses GPT-4.1 by default, but you can switch to any &lt;a href="https://docs.github.com/en/copilot/reference/ai-models/supported-models" rel="noopener noreferrer"&gt;supported Copilot model&lt;/a&gt; — &lt;code&gt;gpt-5&lt;/code&gt;, &lt;code&gt;claude-sonnet-4.5&lt;/code&gt;, etc. — by setting &lt;code&gt;COPILOT_MODEL&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to go beyond Copilot's model lineup entirely, the app supports &lt;a href="https://github.com/github/copilot-sdk/blob/main/docs/guides/setup/byok.md" rel="noopener noreferrer"&gt;BYOK&lt;/a&gt; (Bring Your Own Key). Point it at your own &lt;a href="https://learn.microsoft.com/en-us/azure/ai-foundry/" rel="noopener noreferrer"&gt;Azure AI Foundry&lt;/a&gt; deployment — set your endpoint, API key, and deployment name in &lt;code&gt;.env&lt;/code&gt; and the SDK routes all AI calls to your model. The tool calling, session management, and agent runtime stay identical. You control the model, the billing, and the identity layer; the SDK provides the agentic infrastructure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The app also has experimental support for local OpenAI-compatible servers (Ollama, Foundry Local), though results vary depending on the model's tool calling capabilities, and your environment setup (GPU, etc).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The full source code is on &lt;a href="https://github.com/abhirockzz/cosmosdb-copilot-sdk-time-tracker" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. You can run it with the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Cosmos DB vNext emulator&lt;/a&gt; — no Azure account needed — seed sample data, and start asking questions in natural language. Swap in your own model via BYOK if you want full control over the AI layer.&lt;/p&gt;

&lt;p&gt;The Copilot SDK is open source and available for TypeScript, Python, Go, and .NET. Check out the &lt;a href="https://github.com/github/copilot-sdk/tree/main/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt; to see what else you can build with it.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>github</category>
      <category>agents</category>
    </item>
    <item>
      <title>Agentic Apps with GitHub Copilot SDK</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Mon, 02 Feb 2026 14:45:46 +0000</pubDate>
      <link>https://dev.to/abhirockzz/agentic-apps-with-github-copilot-sdk-60l</link>
      <guid>https://dev.to/abhirockzz/agentic-apps-with-github-copilot-sdk-60l</guid>
      <description>&lt;p&gt;The GitHub Copilot SDK lets you embed Copilot's AI capabilities directly into your apps — available for Go, Python, TypeScript, and .NET.&lt;/p&gt;

&lt;p&gt;Here's a quick demo of building an agentic application with the &lt;a href="https://github.com/github/copilot-sdk" rel="noopener noreferrer"&gt;SDK&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/" rel="noopener noreferrer"&gt;Azure Cosmos DB&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/uDsfdOG-PjU"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flight Diary&lt;/strong&gt; is a sample app where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📸 Upload a boarding pass image → Copilot extracts flight details automatically (no separate OCR/vision API needed)&lt;/li&gt;
&lt;li&gt;🗣️ Query your flight history in plain English → Copilot generates and runs Cosmos DB SQL queries behind the scenes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The part I find interesting is how the SDK handles tool calling. You define the tools, Copilot figures out when and how to use them. For example, when extracting from a boarding pass, it parses the image and calls the save function — all from a single prompt.&lt;/p&gt;

&lt;p&gt;If you want to try it yourself, the code is on &lt;a href="https://github.com/abhirockzz/cosmosdb_copilot_sdk_demo_app" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. It also works with the &lt;a href="https://learn.microsoft.com/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Cosmos DB vNext emulator&lt;/a&gt; for local development.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>go</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>GitHub Copilot CLI + MCP: Talk to Your Database in Plain English</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Sat, 24 Jan 2026 11:56:09 +0000</pubDate>
      <link>https://dev.to/abhirockzz/github-copilot-cli-mcp-talk-to-your-database-in-plain-english-3go4</link>
      <guid>https://dev.to/abhirockzz/github-copilot-cli-mcp-talk-to-your-database-in-plain-english-3go4</guid>
      <description>&lt;p&gt;Here is a quick demo of how to interact with &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/" rel="noopener noreferrer"&gt;Azure Cosmos DB&lt;/a&gt; from &lt;a href="https://github.com/features/copilot/cli" rel="noopener noreferrer"&gt;GitHub Copilot CLI&lt;/a&gt; using MCP. This &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; lets you interact with Azure Cosmos DB using plain English instead of writing queries or clicking through the portal. List databases, create containers, run SQL queries, add items — all through natural language. It works with both the Cosmos DB service and the &lt;a href="https://learn.microsoft.com/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;vNext emulator&lt;/a&gt; (for local development).&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/l6gSYNd1Txs"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The part I really enjoy is watching Copilot CLI figure out which tools to call to get the job done. For example, when I asked for "pending tasks," it generated the query and ran it without me having to think about the syntax.&lt;/p&gt;

&lt;p&gt;If you want to try it yourself, the code is on &lt;a href="//github.com/abhirockzz/mcp_cosmosdb_go"&gt;GitHub&lt;/a&gt;. It's fairly straightforward to build, and set up with &lt;code&gt;/mcp add&lt;/code&gt;. For something production-ready with remote (HTTP) deployment and Entra ID authentication, check out the &lt;a href="https://github.com/AzureCosmosDB/MCPToolKit" rel="noopener noreferrer"&gt;Azure Cosmos DB MCP Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you haven't tried &lt;a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/use-copilot-cli#add-an-mcp-server" rel="noopener noreferrer"&gt;GitHub Copilot CLI with MCP servers&lt;/a&gt; yet, this is an easy way to get started.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>agents</category>
      <category>go</category>
      <category>mcp</category>
      <category>database</category>
    </item>
    <item>
      <title>Azure Cosmos DB Playground: Learn and experiment with queries in your browser</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Wed, 14 Jan 2026 07:04:05 +0000</pubDate>
      <link>https://dev.to/abhirockzz/azure-cosmos-db-playground-learn-and-experiment-with-queries-in-your-browser-1o8j</link>
      <guid>https://dev.to/abhirockzz/azure-cosmos-db-playground-learn-and-experiment-with-queries-in-your-browser-1o8j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Interactive Browser-Based Environment to Learn, Test, and Share Queries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://aka.ms/cosmosdb-playground" rel="noopener noreferrer"&gt;Azure Cosmos DB Playground&lt;/a&gt; is an interactive, browser-based playground for learning and experimenting with &lt;a href="https://learn.microsoft.com/en-us/cosmos-db/query/overview" rel="noopener noreferrer"&gt;Azure Cosmos DB SQL queries&lt;/a&gt; without any setup, installation, or cloud costs. The playground runs on the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Azure Cosmos DB vNext emulator&lt;/a&gt; and leverages the open-source &lt;a href="https://codapi.org/" rel="noopener noreferrer"&gt;codapi&lt;/a&gt; project behind the scenes.&lt;/p&gt;

&lt;p&gt;The playground is a great tool for learning, prototyping, testing, and sharing Azure Cosmos DB queries. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 Explore with pre-loaded datasets and ready-to-run query examples&lt;/li&gt;
&lt;li&gt;📤 Paste or upload your own JSON data to experiment with custom scenarios&lt;/li&gt;
&lt;li&gt;⚡ Instantly see query results and modify both data and queries on the fly&lt;/li&gt;
&lt;li&gt;🔗 Generate shareable links that capture your current dataset and query for easy sharing&lt;/li&gt;
&lt;li&gt;♻️ Restore your last session automatically after a refresh or reopening the page&lt;/li&gt;
&lt;li&gt;🧩 Embed interactive, runnable examples directly into HTML files for documentation, blogs, or tutorials&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is an experimental project and not an official Microsoft or Azure offering. It’s designed for learning and sharing, not for production use.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;▶️ &lt;strong&gt;Quick Demo&lt;/strong&gt; (click to play)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://abhirockzz.github.io/videos/playground_demo.mp4" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fabhirockzz%2Fcosmosdb-playground%2Fraw%2Fmain%2Fimages%2Fplayground.png" alt="Playground Demo" width="800" height="1110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the Playground (or Deploy Your Own) 🚀
&lt;/h2&gt;

&lt;p&gt;You can head over to &lt;strong&gt;&lt;a href="https://aka.ms/cosmosdb-playground" rel="noopener noreferrer"&gt;https://aka.ms/cosmosdb-playground&lt;/a&gt;&lt;/strong&gt; and start experimenting right away!&lt;/p&gt;

&lt;p&gt;While the &lt;a href="https://aka.ms/cosmosdb-playground" rel="noopener noreferrer"&gt;hosted playground&lt;/a&gt; is ready to use, you can also deploy your own instance on Azure. The playground uses a fully containerized architecture with Docker Compose. Follow the detailed deployment instructions available on &lt;a href="https://github.com/abhirockzz/cosmosdb-playground/blob/main/docs/azure-deployment.md" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; to set up your own playground instance.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To configure HTTP(s) access for your deployed playground, refer to the instructions available on &lt;a href="https://github.com/abhirockzz/cosmosdb-playground/blob/main/docs/https-setup.md" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Playground Features&lt;/strong&gt; 🛠️&lt;/p&gt;

&lt;p&gt;These might evolve over time - You can check out the GitHub repository for up to date &lt;a href="https://github.com/abhirockzz/cosmosdb-playground/blob/main/docs/features.md" rel="noopener noreferrer"&gt;list of features&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the Scenes 🏗️
&lt;/h2&gt;

&lt;p&gt;Here is a simplified architecture of the Azure Cosmos DB Playground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------+        +-------------------+        +-------------------+
|   User Browser    |  &amp;lt;---&amp;gt; |      nginx        |  &amp;lt;---&amp;gt; |     Codapi        |
| (playground.html) |        | (Reverse Proxy)   |        | (Sandbox Server)  |
+-------------------+        +-------------------+        +-------------------+
                                                           |
                                                           v
                                                +--------------------------+
                                                |  Ephemeral Query Container|
                                                |  (Python + Cosmos SDK)    |
                                                +--------------------------+
                                                           |
                                                           v
                                                +--------------------------+
                                                | Cosmos DB Emulator       |
                                                | (Docker Container)       |
                                                +--------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At its core, the playground uses the Azure Cosmos DB vNext Emulator as a custom Codapi &lt;a href="https://github.com/nalgeon/codapi/blob/main/docs/add-sandbox.md" rel="noopener noreferrer"&gt;sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Cosmos DB vNext Emulator&lt;/strong&gt;: This is a &lt;strong&gt;local&lt;/strong&gt; version of Azure Cosmos DB (SQL API) available as a Docker container. It &lt;em&gt;emulates&lt;/em&gt; the real Azure Cosmos DB service, so you can experiment with queries and data models without any cloud setup or cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codapi&lt;/strong&gt;: It is a lightweight sandbox server designed for interactive documentation, code examples, and learning environments. In the playground, Codapi manages the creation of isolated Docker containers for each query execution. This ensures that each user’s code and data are executed in an ephemeral environment. Codapi JavaScript widget (&lt;a href="https://github.com/nalgeon/codapi-js" rel="noopener noreferrer"&gt;codapi-js&lt;/a&gt;) is used for the playground frontend integration, which invokes the &lt;a href="https://github.com/nalgeon/codapi/blob/main/docs/api.md" rel="noopener noreferrer"&gt;Codapi API&lt;/a&gt; for code execution in the sandbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query Execution Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nginx&lt;/code&gt; serves the frontend and routes API requests to Codapi, enabling interactive query execution from the browser.&lt;/p&gt;

&lt;p&gt;For each run, Codapi spawns an ephemeral query Docker container where a Python component connects to the long-running Cosmos DB emulator. It creates a temporary Cosmos DB container, seeds the data, executes the query, and returns results. After execution, both the temporary Cosmos DB container and the ephemeral query Docker container are cleaned up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations ⚠️
&lt;/h2&gt;

&lt;p&gt;The playground is a sandbox for learning and experimentation, not production use. It is designed for small(ish) datasets and may have performance constraints, among other limitations. Some of these limitations may change over time. For the most current list, refer to the &lt;a href="https://github.com/abhirockzz/cosmosdb-playground/tree/main/docs" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Out! ✨
&lt;/h2&gt;

&lt;p&gt;Visit &lt;strong&gt;&lt;a href="https://aka.ms/cosmosdb-playground" rel="noopener noreferrer"&gt;https://aka.ms/cosmosdb-playground&lt;/a&gt;&lt;/strong&gt; to start experimenting with queries, and share your examples with others.&lt;/p&gt;

&lt;p&gt;Have feedback or suggestions? Visit the &lt;a href="https://github.com/abhirockzz/cosmosdb-playground" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and share your thoughts. Happy querying!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>python</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build AI Tooling in Go with the MCP SDK – Connecting AI Apps to Databases</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Wed, 07 Jan 2026 17:12:51 +0000</pubDate>
      <link>https://dev.to/abhirockzz/build-ai-tooling-in-go-with-the-mcp-sdk-connecting-ai-apps-to-databases-29if</link>
      <guid>https://dev.to/abhirockzz/build-ai-tooling-in-go-with-the-mcp-sdk-connecting-ai-apps-to-databases-29if</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;A hands‑on walkthrough of building MCP servers that can plug AI applications into Azure Cosmos DB&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://modelcontextprotocol.io/docs/getting-started/intro" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; (MCP) has established itself as the ubiquitous standard for connecting AI applications to external systems. Since its release, there have been &lt;a href="https://modelcontextprotocol.io/docs/sdk" rel="noopener noreferrer"&gt;implementations&lt;/a&gt; across various programming languages and frameworks, enabling developers to build solutions that expose data sources, tools, and workflows to AI applications.&lt;/p&gt;

&lt;p&gt;For Go developers, however, the journey to an official MCP SDK took longer (compared to other SDKs like Python and TypeScript). &lt;a href="https://github.com/orgs/modelcontextprotocol/discussions/224" rel="noopener noreferrer"&gt;Discussions&lt;/a&gt; and &lt;a href="https://github.com/orgs/modelcontextprotocol/discussions/364" rel="noopener noreferrer"&gt;design/implementation&lt;/a&gt; work on the official Go implementation began during early to mid 2025. At the time of writing (January 2026) it stands at version &lt;strong&gt;1.2.0&lt;/strong&gt;. As a Gopher, I'm excited (and relieved!) to finally have a stable, &lt;a href="https://github.com/modelcontextprotocol/go-sdk" rel="noopener noreferrer"&gt;official MCP Go SDK&lt;/a&gt; that the Go community can rely on.&lt;/p&gt;

&lt;p&gt;To explore its capabilities, I built an &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go" rel="noopener noreferrer"&gt;MCP server for Azure Cosmos DB&lt;/a&gt;. This blog post will dive into the MCP Go SDK fundamentals by walking through its specifics, and exploring concepts such as tools, servers, etc. By the end, you'll understand how to use the MCP Go SDK to build your own MCP servers, with Azure Cosmos DB serving as a practical example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This project is not intended to replace the &lt;a href="https://github.com/azure/azure-mcp" rel="noopener noreferrer"&gt;Azure MCP Server&lt;/a&gt; or &lt;a href="https://github.com/AzureCosmosDB/MCPToolKit" rel="noopener noreferrer"&gt;Azure Cosmos DB MCP Toolkit&lt;/a&gt;. Rather, it serves as an experimental &lt;strong&gt;learning tool&lt;/strong&gt; that demonstrates how to combine the Azure and MCP Go SDKs to build AI tooling for Azure Cosmos DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Basics
&lt;/h2&gt;

&lt;p&gt;Let's briefly cover what MCP is and how the MCP Go SDK works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the Model Context Protocol?
&lt;/h3&gt;

&lt;p&gt;The Model Context Protocol (MCP) is an open-source standard for connecting AI applications to external systems. It's often referred to as a &lt;strong&gt;USB-C port for AI applications&lt;/strong&gt; — just as USB-C provides a standardized way to connect devices, MCP provides a standardized way to connect AI applications to data sources, tools, and workflows.&lt;/p&gt;

&lt;p&gt;With MCP, AI applications (ranging from IDEs like VS Code, CLI coding tools like GitHub Copilot or apps like Claude web/desktop) can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access &lt;strong&gt;data sources&lt;/strong&gt; (local files, databases, APIs)&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;tools&lt;/strong&gt; (search engines, calculators, external services)&lt;/li&gt;
&lt;li&gt;Execute &lt;strong&gt;workflows&lt;/strong&gt; (specialized prompts, multi-step operations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This standardization means developers can build MCP servers once and have them work with any MCP-compatible AI application, rather than creating custom integrations for each platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP Go SDK
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/modelcontextprotocol/go-sdk" rel="noopener noreferrer"&gt;official Go MCP SDK&lt;/a&gt; provides the building blocks to create MCP servers and clients in Go. Here's a minimal example of an MCP server with a simple tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/modelcontextprotocol/go-sdk/mcp"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ReverseInput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"text" jsonschema:"the text to reverse"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ReverseOutput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Reversed&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"reversed" jsonschema:"the reversed text"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ReverseText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;ReverseInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ReverseOutput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;runes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;rune&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReverseOutput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Reversed&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;runes&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create server&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"text-tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"v1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Add a tool&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"reverse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"reverses the input text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;ReverseText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Run over stdio&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdioTransport&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example demonstrates the key concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool definition&lt;/strong&gt;: A &lt;code&gt;mcp.Tool&lt;/code&gt; with a name and description&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input/Output types&lt;/strong&gt;: Structs with JSON schema tags that define the tool's interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handler function&lt;/strong&gt;: The actual logic that executes when the tool is called&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server&lt;/strong&gt;: Created with &lt;code&gt;mcp.NewServer()&lt;/code&gt; and configured with tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transport&lt;/strong&gt;: How the server communicates (here using stdio)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These concepts will be covered later on the blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP Server in Action
&lt;/h2&gt;

&lt;p&gt;▶️ To get a sense of what the server can do, take a look at this short demo of using the MCP server with &lt;a href="https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode" rel="noopener noreferrer"&gt;Agent Mode in Visual Studio Code&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/b4foQ_wgmcI"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;This server exposes several tools that enable AI applications to interact with Azure Cosmos DB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_databases&lt;/code&gt; - List all databases in a Cosmos DB account&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list_containers&lt;/code&gt; - List all containers in a specific database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_item&lt;/code&gt; - Read a specific item using its ID and partition key&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute_query&lt;/code&gt; - Execute SQL queries against containers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create_container&lt;/code&gt; - Create new containers with partition keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;add_item_to_container&lt;/code&gt; - Add items to containers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;read_container_metadata&lt;/code&gt; - Retrieve container configuration details&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to setup and configure the server, check out the &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alright, let's dive into how it's built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tools&lt;/strong&gt; are the building blocks of an MCP server. Each tool represents a specific operation that the server can perform.&lt;/p&gt;

&lt;p&gt;Let's use the &lt;code&gt;read_item&lt;/code&gt; tool as an example to understand the fundamental concepts of the MCP Go SDK and how it integrates with the Azure Cosmos DB Go SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP Tools: Definition, Handler, and Execution Flow
&lt;/h3&gt;

&lt;p&gt;An MCP tool consists of these components:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Definition&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The tool definition describes the tool to the AI application. Here's how we define the &lt;code&gt;read_item&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tool&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"read_item"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Read a specific item from a container in an Azure Cosmos DB database using the item ID and partition key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Tool&lt;/code&gt; struct contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: A unique identifier for the tool&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: Helps the AI understand when to use this tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The SDK can automatically infer input and output schemas from your handler function's types, which we'll see next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input and Output Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Type-safe input and output structures define the tool's interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolInput&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Account&lt;/span&gt;      &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"account" jsonschema:"Azure Cosmos DB account name"`&lt;/span&gt;
    &lt;span class="n"&gt;Database&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"database" jsonschema:"Name of the database"`&lt;/span&gt;
    &lt;span class="n"&gt;Container&lt;/span&gt;    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"container" jsonschema:"Name of the container to read data from"`&lt;/span&gt;
    &lt;span class="n"&gt;ItemID&lt;/span&gt;       &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"itemID" jsonschema:"ID of the item to read"`&lt;/span&gt;
    &lt;span class="n"&gt;PartitionKey&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"partitionKey" jsonschema:"Partition key value of the item"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"item" jsonschema:"The item data as JSON string"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK uses these types to automatically generate JSON schemas and handle validation. &lt;strong&gt;JSON tags&lt;/strong&gt; define how fields are serialized, and &lt;strong&gt;jsonschema tags&lt;/strong&gt; provide descriptions that help AI applications understand what each field represents&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Handler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The handler is where the actual work happens. The MCP Go SDK provides a generic &lt;code&gt;AddTool&lt;/code&gt; function that can bind tools to functions with this signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CallToolRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;InputType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CallToolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="n"&gt;OutputType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the &lt;code&gt;read_item&lt;/code&gt; handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 1. Validate inputs&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cosmos db account name missing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"database name missing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c"&gt;// ... more validation&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Get Cosmos DB client&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;GetCosmosClientFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Navigate to the container&lt;/span&gt;
    &lt;span class="n"&gt;databaseClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error creating database client: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;containerClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;databaseClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error creating container client: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 4. Read the item using Cosmos DB SDK&lt;/span&gt;
    &lt;span class="n"&gt;partitionKey&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPartitionKeyString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PartitionKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;itemResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;containerClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ItemID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error reading item: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 5. Return the result&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler handles (pun intended!) several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validates&lt;/strong&gt; input parameters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interacts&lt;/strong&gt; with Azure Cosmos DB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Returns&lt;/strong&gt; structured output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice we return &lt;code&gt;nil&lt;/code&gt; for the &lt;code&gt;*mcp.CallToolResult&lt;/code&gt;. The SDK automatically handles the response marshaling for us. If we return an error, the SDK sets &lt;code&gt;IsError: true&lt;/code&gt; in the result automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authenticating with Azure Cosmos DB
&lt;/h3&gt;

&lt;p&gt;The MCP server uses &lt;code&gt;NewDefaultAzureCredential&lt;/code&gt; from the Azure Identity SDK, which automatically handles multiple authentication methods, such as Azure CLI credentials (for local development), Managed Identity (for production), environment variables, and more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GetCosmosDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;endpoint&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://%s.documents.azure.com:443/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azidentity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error creating credential: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error creating Cosmos client: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have the client, we use the standard Azure Cosmos DB SDK patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;client.NewDatabase()&lt;/code&gt; to get a database client&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;databaseClient.NewContainer()&lt;/code&gt; to get a container client
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;containerClient.ReadItem()&lt;/code&gt; to perform the actual read operation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  MCP Server: Bringing Tools Together
&lt;/h3&gt;

&lt;p&gt;The beauty here is that MCP provides the standardized interface for AI interactions, while the Azure Cosmos DB SDK handles all the database operations – the handler acts as the &lt;strong&gt;bridge&lt;/strong&gt; between these two worlds.&lt;/p&gt;

&lt;p&gt;Now that we understand individual tools, let's see how they're organized within an MCP server. An MCP server exposes specific capabilities (tools, resources, prompts) to AI applications through the standardized MCP protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creating the Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how we create and configure the MCP server in &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go/blob/main/main.go" rel="noopener noreferrer"&gt;main.go&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create the server with metadata&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="s"&gt;"mcp_azure_cosmosdb_go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"Go based MCP server for Azure Cosmos DB using the Azure SDK for Go and the MCP Go SDK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;WebsiteURL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"https://github.com/abhirockzz/mcp_cosmosdb_go"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Register all tools with their handlers&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListDatabases&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListDatabasesToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListContainers&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListContainersToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadContainerMetadata&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadContainerMetadataToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateContainer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateContainerToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddItemToContainer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddItemToContainerToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadItemToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteQuery&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExecuteQueryToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ... transport setup (covered next)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking this down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mcp.NewServer()&lt;/code&gt;&lt;/strong&gt; creates a new server instance with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Implementation metadata&lt;/strong&gt;: Name, title, and version that identify the server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ServerOptions&lt;/strong&gt;: Additional configuration (we use &lt;code&gt;nil&lt;/code&gt; for defaults)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;mcp.AddTool()&lt;/code&gt;&lt;/strong&gt; registers each tool with the server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes the server instance&lt;/li&gt;
&lt;li&gt;The tool definition (from functions like &lt;code&gt;tools.ReadItem()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The handler function (like &lt;code&gt;tools.ReadItemToolHandler&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When the server connects to a client, it automatically advertises the &lt;code&gt;tools&lt;/code&gt; capability, making all registered tools discoverable by the AI application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transport: Connecting Server to Client
&lt;/h3&gt;

&lt;p&gt;A transport defines how the server communicates with clients. It's the communication channel that carries JSON-RPC messages between the server and client. The MCP Go SDK supports multiple transport types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP Streamable Transport&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The server also supports &lt;code&gt;http&lt;/code&gt; transport, which is ideal for web-based AI applications. Here's how we set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Create the streamable HTTP handler&lt;/span&gt;
&lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStreamableHTTPHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Start the HTTP server&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":9090"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Server failed: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;NewStreamableHTTPHandler&lt;/code&gt; creates an HTTP handler that accepts incoming HTTP requests from MCP clients, and returns the appropriate server instance for each request. It handles the streamable transport protocol automatically, and supports multiple concurrent client sessions&lt;/p&gt;

&lt;p&gt;This transport is ideal when you want to support web-based AI applications and the server needs to be accessible over HTTP/HTTPS. This allows multiple clients to connect simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stdio Transport&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another common MCP transport is &lt;strong&gt;stdio&lt;/strong&gt;, used when the server runs as a subprocess:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdioTransport&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stdio transport runs as a subprocess started by the client and communicates via standard input/output streams. It's perfect for local MCP clients like GitHub Copilot CLI, Claude Code (or Desktop), etc. Both transports implement the same MCP protocol, so the server's tools work identically regardless of which transport you choose. The difference is purely in how the server connects to and communicates with clients.&lt;/p&gt;

&lt;p&gt;With the server created, tools registered, and transport configured, the MCP server is ready to accept connections from AI applications and execute operations against Azure Cosmos DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing the MCP Server
&lt;/h2&gt;

&lt;p&gt;This involves verifying functionality at different layers of the stack. This server uses &lt;strong&gt;integration tests&lt;/strong&gt; at two levels: tests that verify the MCP protocol aspects, and tests that focus on handler logic with database interactions. Let's explore both approaches.&lt;/p&gt;

&lt;p&gt;Before diving into testing, let's briefly understand what an MCP client is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding MCP Clients
&lt;/h3&gt;

&lt;p&gt;An &lt;strong&gt;MCP client&lt;/strong&gt; is the component that connects to an MCP server to consume its capabilities. In the context of the MCP server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In production&lt;/strong&gt;: The client is typically an AI application (like Claude Desktop or VS Code) that discovers and calls our tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In testing&lt;/strong&gt;: We create programmatic clients to verify our server works correctly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The MCP Go SDK provides a &lt;a href="https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Client" rel="noopener noreferrer"&gt;Client&lt;/a&gt; type that we can use to connect to our server and call its tools, simulating how a real AI application would interact with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handler-Level Integration Testing with Azure Cosmos DB vNext Emulator
&lt;/h3&gt;

&lt;p&gt;Let's start by looking at tests that focus on handler logic and database interactions. It uses the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Azure Cosmos DB vNext Emulator&lt;/a&gt; with &lt;a href="https://golang.testcontainers.org/" rel="noopener noreferrer"&gt;testcontainers-go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go/blob/main/tools/tools_test.go" rel="noopener noreferrer"&gt;&lt;code&gt;tools_test.go&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestListDatabases&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;           &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;input&lt;/span&gt;          &lt;span class="n"&gt;ListDatabasesToolInput&lt;/span&gt;
        &lt;span class="n"&gt;expectError&lt;/span&gt;    &lt;span class="kt"&gt;bool&lt;/span&gt;
        &lt;span class="n"&gt;expectedResult&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;expectedErrMsg&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"valid account name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ListDatabasesToolInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"dummy_account_does_not_matter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;expectError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;testOperationDBName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"empty account name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ListDatabasesToolInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;expectError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expectedErrMsg&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"cosmos db account name missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ListDatabasesToolHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
                &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectedErrMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Databases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectedResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests call handlers directly (bypassing the MCP protocol layer) and use &lt;a href="https://go.dev/wiki/TableDrivenTests" rel="noopener noreferrer"&gt;table-driven tests&lt;/a&gt; for input validation and error handling, business logic correctness, database operations and edge cases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;setupCosmosEmulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExposedPorts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"8081:8081"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"8080:8080"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;WaitingFor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForListeningPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"8080"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"PROTOCOL"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Started&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="c"&gt;// ... error handling&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;testcontainers-go&lt;/code&gt; library automatically pulls the emulator image, starts the container, and cleans up after tests complete. This is set up once in &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go/blob/main/tools/tools_test.go#L22" rel="noopener noreferrer"&gt;TestMain&lt;/a&gt; function and shared across all tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  MCP Protocol Integration Testing
&lt;/h3&gt;

&lt;p&gt;Beyond handler testing, we also verify the complete MCP protocol stack—from client request through server processing to response. Here's an example from &lt;a href="https://github.com/abhirockzz/mcp_cosmosdb_go/blob/main/tools/mcp_integration_test.go" rel="noopener noreferrer"&gt;mcp_integration_test.go&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMCPIntegration_ReadItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 1. Create MCP server and register the read_item tool&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"test-cosmosdb-server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Create in-memory transports for testing&lt;/span&gt;
    &lt;span class="n"&gt;serverTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientTransport&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewInMemoryTransports&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Connect server&lt;/span&gt;
    &lt;span class="n"&gt;serverSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serverTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;serverSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 4. Create and connect client&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Implementation&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"test-client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;clientSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;clientSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// 5. Call the tool via MCP protocol&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;clientSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallTool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CallToolParams&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"read_item"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"account"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"dummy_account_does_not_matter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"database"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;testOperationDBName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;testOperationContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"itemID"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"partitionKey"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;partitionKeyValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// 6. Verify the response&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;False&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 7. Parse and validate the JSON response&lt;/span&gt;
    &lt;span class="n"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;ReadItemToolResult&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textContent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test demonstrates several key concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Transports&lt;/strong&gt;: &lt;code&gt;mcp.NewInMemoryTransports()&lt;/code&gt; creates a pair of connected transports without requiring actual network communication—perfect for testing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client-Server Connection&lt;/strong&gt;: Both server and client connect to their respective transports, establishing a session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool Invocation&lt;/strong&gt;: &lt;code&gt;clientSession.CallTool()&lt;/code&gt; sends a properly formatted MCP request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response Handling&lt;/strong&gt;: The result is parsed from the MCP protocol format back to our domain types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Protocol Verification&lt;/strong&gt;: This tests the complete round trip: request serialization → tool execution → response serialization → client parsing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both handler-level and protocol-level tests use the Azure Cosmos DB vNext emulator, not mocks. Handler-level tests provide feedback on business logic, while protocol-level tests ensure MCP compliance and end-to-end functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;With the MCP Go SDK, building MCP servers has become significantly more accessible for Go developers. You don't have to &lt;em&gt;go for Python&lt;/em&gt; anymore (sorry Pythonistas, pun intended!).&lt;/p&gt;

&lt;p&gt;This MCP server demonstrates how to combine the MCP Go SDK with domain-specific tools — in this case, the Azure Cosmos DB Go SDK. While this server provides useful functionality for interacting with Cosmos DB from AI applications, &lt;strong&gt;its primary purpose is educational&lt;/strong&gt;. As mentioned before, this is a learning tool that shows how to integrate MCP with real-world services, not a replacement for solutions like the &lt;a href="https://github.com/Azure/azure-mcp-server" rel="noopener noreferrer"&gt;Azure MCP Server&lt;/a&gt; or the &lt;a href="https://github.com/AzureCosmosDB/MCPToolKit" rel="noopener noreferrer"&gt;Azure Cosmos DB MCP Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The specific patterns we covered (defining tools, implementing handlers, managing authentication, choosing transports, and writing integration tests) apply to any MCP server you might build. The same concepts apply, whether you're exposing APIs, databases, file systems, or custom business logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next Steps
&lt;/h3&gt;

&lt;p&gt;Ready to build your own MCP server? Here are some resources to get you started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP Go SDK resources&lt;/strong&gt;: &lt;a href="https://github.com/modelcontextprotocol/go-sdk/tree/main/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;, &lt;a href="https://github.com/modelcontextprotocol/go-sdk/tree/main/design" rel="noopener noreferrer"&gt;design&lt;/a&gt;, and &lt;a href="https://github.com/modelcontextprotocol/go-sdk/tree/main/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Specification&lt;/strong&gt;: &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;https://modelcontextprotocol.io/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Cosmos DB Go SDK&lt;/strong&gt;: &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos" rel="noopener noreferrer"&gt;https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Azure Cosmos DB vNext Emulator and Testcontainers Guide&lt;/strong&gt;: &lt;a href="https://devblogs.microsoft.com/cosmosdb/integration-testing-for-go-applications-using-testcontainers-and-containerized-databases/" rel="noopener noreferrer"&gt;Integration Testing for Go Applications&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation" rel="noopener noreferrer"&gt;MCP ecosystem is growing rapidly&lt;/a&gt;, and I am excited for Go developers who now have first-class support for participating in this evolution!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>go</category>
      <category>agents</category>
    </item>
    <item>
      <title>Azure Cosmos DB vNext Emulator: Query and Observability Enhancements</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Sat, 13 Dec 2025 15:28:48 +0000</pubDate>
      <link>https://dev.to/abhirockzz/azure-cosmos-db-vnext-emulator-query-and-observability-enhancements-5b9a</link>
      <guid>https://dev.to/abhirockzz/azure-cosmos-db-vnext-emulator-query-and-observability-enhancements-5b9a</guid>
      <description>&lt;p&gt;The &lt;a href="https://learn.microsoft.com/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Azure Cosmos DB Linux-based vNext emulator (preview)&lt;/a&gt; is a local version of the Azure Cosmos DB service that runs as a Docker container on Linux, macOS, and Windows. It provides a cost-effective way to develop and test applications locally without requiring an Azure subscription or network connectivity.&lt;/p&gt;

&lt;p&gt;The latest release brings improvements in two key areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Query engine&lt;/strong&gt;: Addresses limitations in JOINs, operators, subdocument handling, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: OpenTelemetry support to collect and correlate metrics, logs, and traces for deeper insight into applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/knbyvYWcZZA"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Query Improvements
&lt;/h2&gt;

&lt;p&gt;This emulator release enables several query patterns that were previously unsupported. In this post, we'll focus on the following enhancements to query capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Improved JOIN Operations&lt;/strong&gt; – Support for nested JOINs across multiple array levels, cross-product JOINs, and self-JOINs on primitive arrays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Operator Support&lt;/strong&gt; – Support for string manipulation functions (&lt;code&gt;CONCAT&lt;/code&gt;, &lt;code&gt;LENGTH&lt;/code&gt;) and array operations (&lt;code&gt;ARRAY_LENGTH&lt;/code&gt;, direct array indexing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Subdocument Handling&lt;/strong&gt; – Improved querying of deeply nested object properties and proper handling of missing properties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's explore these with practical examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improved JOINs
&lt;/h3&gt;

&lt;p&gt;In Azure Cosmos DB, &lt;a href="https://learn.microsoft.com/en-us/cosmos-db/query/" rel="noopener noreferrer"&gt;JOINs&lt;/a&gt; enable you to flatten and traverse data within documents, allowing you to work with nested and hierarchical data structures.&lt;/p&gt;

&lt;p&gt;Let's explore this using a sample family dataset where each document represents a family with nested arrays. This includes three families (Andersen, Wakefield, and Miller) with varying numbers of children and pets, demonstrating how JOINs handle different data structures. Notice the hierarchical nesting: each family has a children array, each child has a pets array, and families also have a separate tags array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AndersenFamily"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Andersen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Thomas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"father"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mary Kay"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mother"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Henriette"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"pets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fluffy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rabbit"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"seattle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"family-friendly"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Seattle"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WakefieldFamily"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wakefield"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Robin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mother"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ben"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"father"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jesse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"pets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Goofy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dog"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Shadow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Horse"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lisa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"pets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"newyork"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"urban"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New York"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MillerFamily"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Miller"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"David"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"relationship"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"father"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Emma"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"grade"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"pets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Whiskers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cat"&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"boston"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"academic"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Boston"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Basic JOIN – Flattening Arrays&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example, if you need to generate a list of all children across multiple families for a school roster or activity planning, use the &lt;code&gt;JOIN&lt;/code&gt; operator to flatten the children array, creating one result row per child across all family documents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grade&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns one row per child (giving you a flattened view of all children with their family information) – Henriette from the Andersen family, Jesse and Lisa from the Wakefield family, and Emma from the Miller family.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JOIN with Filter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To find children within specific grade ranges, add a &lt;code&gt;WHERE&lt;/code&gt; clause to &lt;code&gt;JOIN&lt;/code&gt;. This lets you filter results based on properties of the array elements, like selecting only middle school students (grade 6 and above).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grade&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;grade&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result set includes only Jesse (grade 8) and Emma (grade 6), automatically excluding Henriette and Lisa who are in lower grades.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested JOIN – Traversing Hierarchical Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you need to traverse multiple levels of your data structure to identify all pets and their owners, use chained JOINs to drill down through nested arrays, moving from families to children to pets in a single query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see each pet listed with its owner and family: Henriette's rabbit Fluffy, Jesse's dog Goofy and horse Shadow, and Emma's cat Whiskers. Notice that Lisa doesn't appear in the results since her pets array is empty, demonstrating the &lt;code&gt;INNER JOIN&lt;/code&gt; behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross Product JOIN – Combining Independent Arrays&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To associate every child with all of their family's characteristics or tags to understand patterns and preferences, join two arrays from the same document to create a cross product pairing each child with each family tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates every possible child-tag pairing within each family: Henriette appears three times (paired with "seattle", "active", and "family-friendly"), Jesse and Lisa each appear twice (with "newyork" and "urban"), and Emma appears twice (with "boston" and "academic").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JOIN on Primitive Arrays&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Analyzing common characteristics across your entire dataset requires flattening simple value arrays. JOINs work on arrays of primitive values like strings or numbers, in addition to complex objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each tag becomes its own row associated with its family: the Andersen family contributes three tags ("seattle", "active", "family-friendly"), while Wakefield and Miller each contribute two tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-JOIN – Finding Combinations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Analyzing which characteristics tend to appear together in your data (such as discovering that families tagged "urban" are also often tagged "active") requires finding unique pairs within the same array. You achieve this by joining an array with itself using different aliases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tag1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tag2&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Andersen family yields three unique pairs from their three tags ("active"+"seattle", "active"+"family-friendly", "family-friendly"+"seattle"), while Wakefield and Miller each produce one pair from their two tags – perfect for discovering which characteristics commonly appear together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex Filters – Multi-Level Conditions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Real-world queries often require filtering on multiple criteria at different nesting levels, like finding all rabbits owned by families in Washington state for a local pet adoption program. You can combine conditions on both root document properties (location) and deeply nested array elements (pet type) in a single query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt; 
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pets&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'WA'&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Rabbit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query pinpoints exactly what you're looking for: Henriette's rabbit Fluffy in the Andersen family from Washington state, filtering out all other pets and families in one efficient operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code sample&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Python example demonstrating these &lt;code&gt;JOIN&lt;/code&gt; scenarios is available in this GitHub Gist: &lt;a href="https://gist.github.com/abhirockzz/76766da88203edfa6181ab67a9dae8a5#file-cosmosdb_join_examples-py" rel="noopener noreferrer"&gt;&lt;code&gt;cosmosdb_join_examples.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the example, start the Azure Cosmos DB Emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234:1234 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview &lt;span class="nt"&gt;--protocol&lt;/span&gt; http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download and run the script from the gist – it automatically sets up the database, loads the sample data, and runs each query scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python cosmosdb_join_examples.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Enhanced Operator Support
&lt;/h3&gt;

&lt;p&gt;The emulator now supports operators related to string manipulation, array operations and more. Let's explore these capabilities using an employee dataset where each document contains personal information, skill tags, and performance scores.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@company.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"senior"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"manager"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;85&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob@company.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"junior"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"developer"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;91&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Charlie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"charlie@company.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"senior"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"architect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onsite"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scores"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;95&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;89&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;String Operations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Build formatted contact lists by combining multiple fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;CONCAT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;' - '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;contact&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces formatted strings like "Alice – &lt;a href="mailto:alice@company.com"&gt;alice@company.com&lt;/a&gt;", useful for generating reports or user-facing displays.&lt;/p&gt;

&lt;p&gt;Filter records based on text length for data validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns only "Charlie" since it's the only name exceeding 5 characters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Array Operations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Count elements in nested arrays to identify employees with multiple skills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARRAY_LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;skillCount&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the number of tags each employee has – Alice and Charlie each have 3 skills, while Bob has 2.&lt;/p&gt;

&lt;p&gt;Query specific array positions to filter by criteria like first performance score:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;85&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns "Charlie" whose first score (95) exceeds the threshold, enabling queries on specific elements without flattening the entire array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code sample&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Python example demonstrating these operator scenarios above is available in this GitHub Gist: &lt;a href="https://gist.github.com/abhirockzz/76766da88203edfa6181ab67a9dae8a5#file-cosmosdb_operator_examples-py" rel="noopener noreferrer"&gt;&lt;code&gt;cosmosdb_operator_examples.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the example, start the Azure Cosmos DB Emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234:1234 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview &lt;span class="nt"&gt;--protocol&lt;/span&gt; http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download and run the script from the gist – it automatically sets up the database, loads the sample data, and runs each query scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python cosmosdb_operator_examples.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Improved Subdocument Queries
&lt;/h3&gt;

&lt;p&gt;The emulator now provides better support for querying nested object properties and subdocuments. Let's explore these capabilities using a user profile dataset where each document contains nested profile information, contact details, and settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"555-0101"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"settings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"light"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Selecting Nested Properties with IS_DEFINED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This query selects nested properties while ensuring the parent object exists. It returns all usernames with their full names, correctly handling documents with defined profile objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fullName&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;IS_DEFINED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Checking Nested Properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To identify users with complete contact information before processing, use this query that returns only users who have the profile.contact defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;IS_DEFINED&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handling Missing Properties&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cases where certain nested properties don't exist need to be handled gracefully. This query accesses a property that may not be present in all documents. It returns the document with the phone property omitted from results when it doesn't exist.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code sample&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A Python example demonstrating these subdocument query scenarios above is available in this GitHub Gist: &lt;a href="https://gist.github.com/abhirockzz/76766da88203edfa6181ab67a9dae8a5#file-cosmosdb_subdocument_examples-py" rel="noopener noreferrer"&gt;&lt;code&gt;cosmosdb_subdocument_examples.py&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To run the example, start the Azure Cosmos DB Emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234:1234 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview &lt;span class="nt"&gt;--protocol&lt;/span&gt; http
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download and run the script from the gist – it automatically sets up the database, loads the sample data, and runs each query scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python cosmosdb_subdocument_examples.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  OpenTelemetry for Observability
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is an open-source observability framework that provides a collection of tools, APIs, and SDKs for instrumenting, generating, collecting, and exporting telemetry data.&lt;/p&gt;

&lt;p&gt;To activate OpenTelemetry in the Azure Cosmos DB Emulator, set the &lt;code&gt;"--enable-otlp"&lt;/code&gt; flag to &lt;code&gt;"true"&lt;/code&gt; (or &lt;code&gt;ENABLE_OTLP_EXPORTER&lt;/code&gt; environment variable) when starting the Docker container. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8081 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-p&lt;/span&gt; 1234:1234 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview &lt;span class="nt"&gt;--protocol&lt;/span&gt; http &lt;span class="nt"&gt;--enable-otlp&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can also use the &lt;code&gt;"--enable-console"&lt;/code&gt; flag (or use the &lt;code&gt;ENABLE_CONSOLE_EXPORTER&lt;/code&gt; environment variable) to enable console output of telemetry data that can be useful for debugging purposes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Use Docker Compose to simplify OpenTelemetry setup with the Azure Cosmos DB Emulator. The configuration below connects the emulator with Jaeger (distributed tracing) and Prometheus (metrics collection):&lt;/p&gt;

&lt;p&gt;Copy and save the following in a &lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;jaeger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jaegertracing/jaeger:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jaeger&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;16686:16686"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4317:4317"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4318:4318"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cosmosdb-network&lt;/span&gt;

  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prom/prometheus:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9090:9090"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./prometheus.yml:/etc/prometheus/prometheus.yml&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cosmosdb-network&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--config.file=/etc/prometheus/prometheus.yml'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--web.enable-otlp-receiver'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--storage.tsdb.path=/prometheus'&lt;/span&gt;

  &lt;span class="na"&gt;cosmosdb_emulator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cosmosdb_emulator&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1234:1234"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;9712:9712"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8889:8889"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ENABLE_OTLP_EXPORTER=true&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ENABLE_CONSOLE_EXPORTER=true&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cosmosdb-network&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cosmosdb-network&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, make sure to create a &lt;code&gt;prometheus.yml&lt;/code&gt; file in the same directory as your &lt;code&gt;docker-compose.yml&lt;/code&gt; with the following content – this will ensure that Prometheus can scrape metrics from the Azure Cosmos DB Emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;global&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;

&lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cosmosdb-emulator-metrics'&lt;/span&gt;
    &lt;span class="na"&gt;scrape_interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cosmosdb_emulator:8889'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the emulator is already running, stop it first. Then start the entire stack (Azure Cosmos DB Emulator, Jaeger, and Prometheus) using Docker Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the services to start, and run the sample Python script from the previous sections (a few times) – this will generate some telemetry data.&lt;/p&gt;

&lt;p&gt;Check the Jaeger UI at &lt;strong&gt;&lt;a href="http://localhost:16686" rel="noopener noreferrer"&gt;http://localhost:16686&lt;/a&gt;&lt;/strong&gt; to view distributed traces, search for specific operations, analyze request latency, and inspect the execution flow of your queries through the Azure Cosmos DB Emulator:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yvymfcsw94zp47y72wc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yvymfcsw94zp47y72wc.png" alt="Jaeger portal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the Prometheus UI at &lt;strong&gt;&lt;a href="http://localhost:9090" rel="noopener noreferrer"&gt;http://localhost:9090&lt;/a&gt;&lt;/strong&gt; to query and visualize metrics, monitor request rates, track query performance statistics, and analyze resource utilization patterns from the Azure Cosmos DB Emulator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3l5qolzcno2xyzgt7o4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk3l5qolzcno2xyzgt7o4.png" alt="Prometheus portal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For details, refer to the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux#opentelemetry-support" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Health Probe
&lt;/h2&gt;

&lt;p&gt;The emulator exposes health check endpoints on port &lt;code&gt;"8080"&lt;/code&gt; that return a HTTP &lt;code&gt;200 OK&lt;/code&gt; response when all components (Gateway, Data Explorer, etc.) are fully operational.&lt;/p&gt;

&lt;p&gt;This is particularly valuable for integration testing frameworks like &lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt;, which provide &lt;a href="https://golang.testcontainers.org/features/wait/introduction/" rel="noopener noreferrer"&gt;waiting strategies&lt;/a&gt; to ensure containers are ready before tests run. Instead of using arbitrary sleep delays or log messages (which are unreliable since they may change), you can configure a waiting strategy that will check if the emulator Docker container is listening to the &lt;code&gt;"8080"&lt;/code&gt; health check port.&lt;/p&gt;

&lt;p&gt;Here's a Go example using &lt;code&gt;testcontainers-go&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;emulatorImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExposedPorts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"8081:8081"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"8080:8080"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;WaitingFor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForListeningPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"8080"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="c"&gt;// Wait for health check port&lt;/span&gt;
        &lt;span class="c"&gt;// WaitingFor: wait.ForLog("PostgreSQL=OK, Gateway=OK, Explorer=OK, Ready=YES"), //this works but is less reliable, hence not recommended&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Started&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Create credential and client&lt;/span&gt;
    &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emulatorKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClientWithKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emulatorEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cred&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Available endpoints:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:8080/alive" rel="noopener noreferrer"&gt;http://localhost:8080/alive&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Liveness probe&lt;/td&gt;
&lt;td&gt;Returns 503 if PostgreSQL and Gateway are unhealthy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:8080/ready" rel="noopener noreferrer"&gt;http://localhost:8080/ready&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Readiness probe&lt;/td&gt;
&lt;td&gt;Returns 503 if any component is not operational (disabled components like Explorer are excluded)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="http://localhost:8080/status" rel="noopener noreferrer"&gt;http://localhost:8080/status&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Status information&lt;/td&gt;
&lt;td&gt;Always returns 200 with detailed JSON status for all components&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The latest Azure Cosmos DB Emulator release eliminates key query limitations and adds observability features, enabling you to develop and test more complex scenarios locally. You can now implement advanced data access patterns with nested JOINs, leverage operator support for string and array manipulation, and monitor your application's behavior with OpenTelemetry. You can start using these capabilities to accelerate your development workflow and catch issues earlier in your development cycle.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>database</category>
      <category>azure</category>
      <category>python</category>
    </item>
    <item>
      <title>DocumentDB goes cloud-native: Introducing the DocumentDB Kubernetes Operator</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Mon, 10 Nov 2025 08:49:43 +0000</pubDate>
      <link>https://dev.to/abhirockzz/documentdb-goes-cloud-native-introducing-the-documentdb-kubernetes-operator-3ji9</link>
      <guid>https://dev.to/abhirockzz/documentdb-goes-cloud-native-introducing-the-documentdb-kubernetes-operator-3ji9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Announced and published on &lt;a href="https://opensource.microsoft.com/blog/2025/11/05/documentdb-goes-cloud-native-introducing-the-documentdb-kubernetes-operator" rel="noopener noreferrer"&gt;https://opensource.microsoft.com/blog/2025/11/05/documentdb-goes-cloud-native-introducing-the-documentdb-kubernetes-operator&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today, we're excited to announce the &lt;a href="https://aka.ms/documentdb-kubernetes-operator" rel="noopener noreferrer"&gt;DocumentDB Kubernetes Operator&lt;/a&gt;, an open-source, cloud-native solution to deploy, manage, and scale DocumentDB instances on Kubernetes. &lt;a href="https://github.com/documentdb/documentdb" rel="noopener noreferrer"&gt;DocumentDB&lt;/a&gt; is a MongoDB-compatible, open-source document database built on PostgreSQL. The DocumentDB Kubernetes Operator represents a natural evolution of the DocumentDB ecosystem, following our &lt;a href="https://opensource.microsoft.com/blog/2025/01/23/documentdb-open-source-announcement/" rel="noopener noreferrer"&gt;open source announcement&lt;/a&gt; and &lt;a href="https://opensource.microsoft.com/blog/2025/08/25/documentdb-joins-the-linux-foundation/" rel="noopener noreferrer"&gt;recent joining of the Linux Foundation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When it comes to distributed databases, there is no one-size-fits-all solution. Database-as-a-Service (DBaaS) options may not always meet customers' data sovereignty or portability needs. On the other hand, managing database clusters manually is complex and resource intensive. What’s needed is a balanced approach that automates routine tasks like updates and backups, while simplifying operations such as scaling, failover, and recovery. This is precisely where Kubernetes excels—bridging automation with operational simplicity.&lt;/p&gt;

&lt;p&gt;However, unlike stateless applications that can be easily scaled and replaced, running stateful workloads in Kubernetes has always posed unique challenges. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The DocumentDB Kubernetes Operator addresses these by using the operator pattern to extend Kubernetes, making it possible to manage DocumentDB clusters as native Kubernetes resources.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This approach creates a clear separation of responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The database platform team can focus solely on system health.&lt;/li&gt;
&lt;li&gt;App developers enjoy a DBaaS-like experience, without the need to build custom automation between container orchestration and database operations.&lt;/li&gt;
&lt;li&gt;The operator handles the complexity of PostgreSQL cluster orchestration, MongoDB protocol translation, and other critical operations.&lt;/li&gt;
&lt;li&gt;Application development teams can integrate services using MongoDB-compatible drivers and tools, thereby simplifying the process of migrating existing workloads to DocumentDB, or building new cloud-native applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  DocumentDB operator architecture overview
&lt;/h2&gt;

&lt;p&gt;To understand how this works, let’s take a look under the hood, to explore the key components and architecture that make this seamless Kubernetes integration possible.&lt;/p&gt;

&lt;p&gt;A DocumentDB cluster deployed on Kubernetes consists of multiple DocumentDB instances that are orchestrated by the operator. A DocumentDB instance consists of the following core components that run inside a Kubernetes Pod:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL with DocumentDB Extension&lt;/strong&gt;: This is the &lt;a href="https://github.com/documentdb/documentdb" rel="noopener noreferrer"&gt;core database engine&lt;/a&gt; enhanced with document storage and querying capabilities.It is deployed in customer application namespaces on Kubernetes worker nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gateway Container&lt;/strong&gt;: A protocol translator that runs as a sidecar container, converting MongoDB wire protocol requests into PostgreSQL DocumentDB extension calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, the DocumentDB instance is accessible within the cluster. If configured, the operator creates a &lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/" rel="noopener noreferrer"&gt;Kubernetes Service&lt;/a&gt; for external client applications to connect to the DocumentDB cluster (via the Gateway) using any MongoDB-compatible client or tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudNative-PG operator for PostgreSQL orchestration
&lt;/h2&gt;

&lt;p&gt;The DocumentDB operator uses the &lt;a href="https://cloudnative-pg.io/documentation/1.27/" rel="noopener noreferrer"&gt;CloudNative-PG (CNPG)&lt;/a&gt; operator for PostgreSQL cluster management. CNPG is a Cloud Native Computing Foundation &lt;a href="https://www.cncf.io/sandbox-projects/" rel="noopener noreferrer"&gt;(CNCF) Sandbox project&lt;/a&gt; that provides an open-source Kubernetes operator for managing PostgreSQL workloads. The CNPG operator runs in the cnpg-system namespace on Kubernetes worker nodes. Behind the scenes, the DocumentDB operator creates the required CNPG resources to manage the lifecycle of PostgreSQL instances with the DocumentDB extension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lh0i1sjxvtgeobcu8p9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lh0i1sjxvtgeobcu8p9.png" alt="High level overview of DocumentDB cluster deployment on Kubernetes" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The operator also includes a CNPG Sidecar Injector component, which is an admission webhook that automatically injects the DocumentDB Gateway container into PostgreSQL pods during deployment. Thanks to the extensibility of CNPG, the DocumentDB gateway container is implemented as a &lt;a href="https://github.com/cloudnative-pg/cnpg-i" rel="noopener noreferrer"&gt;CloudNativePG Interface&lt;/a&gt; (CNPG-I) plugin.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;DocumentDB is addressing a real need as an open-source, document-oriented NoSQL database built on PostgreSQL. By offering MongoDB API compatibility without vendor lock-in, it tackles a long-standing challenge for developers. We are thrilled to see the DocumentDB Kubernetes Operator joining the Linux Foundation, and proud that under the hood, it's powered by CloudNativePG, a CNCF Sandbox project. The future of PostgreSQL on Kubernetes just got even brighter!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gabriele Bartolini, Vice President, EDB&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting started with DocumentDB Kubernetes Operator
&lt;/h2&gt;

&lt;p&gt;Ready to try it out? Getting started with the operator is straightforward. You can use a local Kubernetes cluster such as &lt;a href="https://minikube.sigs.k8s.io/" rel="noopener noreferrer"&gt;minikube&lt;/a&gt; or &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;kind&lt;/a&gt; and use &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; for installation.&lt;/p&gt;

&lt;p&gt;First, execute the commands below to install &lt;a href="https://cert-manager.io/docs/" rel="noopener noreferrer"&gt;cert-manager&lt;/a&gt; to manage TLS certificates for the DocumentDB cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm repo add jetstack https://charts.jetstack.io

helm repo update

helm &lt;span class="nb"&gt;install &lt;/span&gt;cert-manager jetstack/cert-manager &lt;span class="nt"&gt;--namespace&lt;/span&gt; cert-manager &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;installCRDs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install the DocumentDB operator using the Helm chart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;documentdb-operator oci://ghcr.io/microsoft/documentdb-operator &lt;span class="nt"&gt;--namespace&lt;/span&gt; documentdb-operator &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This will install the latest version of the operator. To specify a version, use -- version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Wait for the operator to start. Run this command to verify its status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                                 READY   STATUS    RESTARTS  AGE

documentdb-operator-65d6b97878-ns5wk 1/1     Running   0         1m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/" rel="noopener noreferrer"&gt;Kubernetes Secret&lt;/a&gt; to store the DocumentDB credentials. This should have your desired administrator username and password (make sure to note them down):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: documentdb-preview-ns
---
apiVersion: v1
kind: Secret
metadata:
  name: documentdb-credentials
  namespace: documentdb-preview-ns
type: Opaque
stringData:
  username: k8s_secret_user
  password: DemoPwd
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the credentials in place, create a single-node DocumentDB cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: documentdb-preview-ns
---
apiVersion: db.microsoft.com/preview
kind: DocumentDB
metadata:
  name: documentdb-preview
  namespace: documentdb-preview-ns
spec:
  nodeCount: 1
  instancesPerNode: 1
  documentDbCredentialSecret: documentdb-credentials
  resource:
    storage:
      pvcSize: 10Gi
  exposeViaService:
    serviceType: ClusterIP
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the DocumentDB cluster to be fully initialized, and run this command to verify that it is running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                   READY   STATUS    RESTARTS   AGE

documentdb-preview-1   2/2     Running   0          1m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the cluster is running, you can connect to the DocumentDB instance directly through the Gateway port &lt;code&gt;10260&lt;/code&gt;. For both &lt;code&gt;minikube&lt;/code&gt; and &lt;code&gt;kind&lt;/code&gt;, this can be easily done using &lt;a href="https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/" rel="noopener noreferrer"&gt;port forwarding&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward pod/documentdb-preview-1 10260:10260 &lt;span class="nt"&gt;-n&lt;/span&gt; documentdb-preview-ns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With port forwarding active, you can now connect using any MongoDB client or tool. For example, from a different terminal, try connecting with mongosh (MongoDB shell):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mongosh 127.0.0.1:10260 &lt;span class="nt"&gt;-u&lt;/span&gt; k8s_secret_user &lt;span class="nt"&gt;-p&lt;/span&gt; DemoPwd &lt;span class="nt"&gt;--authenticationMechanism&lt;/span&gt; SCRAM-SHA-256 &lt;span class="nt"&gt;--tls&lt;/span&gt; &lt;span class="nt"&gt;--tlsAllowInvalidCertificates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Join us in our mission to advance the open-source document database ecosystem!
&lt;/h2&gt;

&lt;p&gt;The DocumentDB Kubernetes Operator represents an important milestone in our broader mission and our commitment to vendor-neutral, community-driven development that puts developer needs first.&lt;/p&gt;

&lt;p&gt;We invite you to join the community and help shape the future of cloud-native document databases.&lt;/p&gt;

&lt;p&gt;Get started by exploring &lt;a href="https://aka.ms/documentdb-kubernetes-operator" rel="noopener noreferrer"&gt;the GitHub repository&lt;/a&gt;, &lt;a href="https://documentdb.io/documentdb-kubernetes-operator" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, or participate in discussions on our &lt;a href="https://aka.ms/documentdb-kubernetes-operator-discord" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;. As the project continues to evolve under Linux Foundation governance, you can expect to see contributions that expand functionality and integrate with other Kubernetes and CNCF projects.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>opensource</category>
      <category>database</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Build a RAG application with LangChain and Local LLMs powered by Ollama</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Fri, 22 Aug 2025 14:12:59 +0000</pubDate>
      <link>https://dev.to/abhirockzz/build-a-rag-application-with-langchain-and-local-llms-powered-by-ollama-3el5</link>
      <guid>https://dev.to/abhirockzz/build-a-rag-application-with-langchain-and-local-llms-powered-by-ollama-3el5</guid>
      <description>&lt;p&gt;Local large language models (LLMs) provide significant advantages for developers and organizations. Key benefits include enhanced &lt;strong&gt;data privacy&lt;/strong&gt;, as sensitive information remains entirely within your own infrastructure, and &lt;strong&gt;offline functionality&lt;/strong&gt;, enabling uninterrupted work even without internet access. While cloud-based LLM services are convenient, running models locally gives you full control over model behavior, performance tuning, and potential cost savings. This make them ideal for experimentation before running production workloads.&lt;/p&gt;

&lt;p&gt;The ecosystem for local LLMs has matured significantly, with several excellent options available, such as &lt;a href="https://ollama.com/" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-local/get-started" rel="noopener noreferrer"&gt;Foundry Local&lt;/a&gt;, &lt;a href="https://docs.docker.com/ai/model-runner/" rel="noopener noreferrer"&gt;Docker Model Runner&lt;/a&gt;, and more. Most popular AI/Agent frameworks including &lt;a href="https://python.langchain.com/docs/how_to/local_llms/" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; and &lt;a href="https://langchain-ai.github.io/langgraph/tutorials/rag/langgraph_self_rag_local" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; provide integration with these local model runners, making it easier to integrate them into your projects.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/AQ-h1JHaX7I"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What will you learn?
&lt;/h2&gt;

&lt;p&gt;This blog post will illustrate how to use local LLMs with &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/gen-ai/why-cosmos-ai" rel="noopener noreferrer"&gt;Azure Cosmos DB as a vector database&lt;/a&gt; for retrieval-augmented generation (RAG) scenarios. It will guide you through setting up a local LLM solution, configuring Azure Cosmos DB, loading data, performing vector searches, and executing RAG queries. You can either use the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator" rel="noopener noreferrer"&gt;Azure Cosmos DB emulator&lt;/a&gt; for local development or connecting to an Azure Cosmos DB account in the cloud. You will be using Ollama (open-source solution) to run LLMs locally on your own machine. It lets you download, run, and interact with a variety of LLMs (like Llama 3, Mistral, and others) using simple commands, without needing cloud access or complex setup.&lt;/p&gt;

&lt;p&gt;By the end of this blog post, you will have a working local RAG setup that leverages Ollama and Azure Cosmos DB. the sample app uses &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/gen-ai/integrations?context=%2Fazure%2Fcosmos-db%2Fnosql%2Fcontext%2Fcontext" rel="noopener noreferrer"&gt;LangChain integration with Azure Cosmos DB&lt;/a&gt; to perform embedding, data loading, and vector search. You can easily adapt it to other frameworks like LlamaIndex.&lt;/p&gt;

&lt;p&gt;Alright, lets dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Ollama
&lt;/h2&gt;

&lt;p&gt;To get started with Ollama, follow the &lt;a href="https://github.com/ollama/ollama?tab=readme-ov-file#ollama" rel="noopener noreferrer"&gt;official installation guide&lt;/a&gt; on GitHub to install it on your system. The installation process is straightforward across different platforms. For example, on Linux systems, you can install Ollama with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, start the Ollama service by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This blog post demonstrates the integration using two specific models from the Ollama library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/library/mxbai-embed-large" rel="noopener noreferrer"&gt;mxbai-embed-large&lt;/a&gt;&lt;/strong&gt; - A high-quality embedding model with 1024 dimensions, ideal for generating vector representations of text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/library/llama3:8b" rel="noopener noreferrer"&gt;llama3&lt;/a&gt;&lt;/strong&gt; - The 8B parameter variant of Meta's Llama 3, which serves as our chat model for the RAG pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Download both models using the following commands. Note that this process may take several minutes depending on your internet connection speed, as these are substantial model files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull mxbai-embed-large
ollama pull llama3:8b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Something to keep in mind ...
&lt;/h3&gt;

&lt;p&gt;While tools like Ollama make it straightforward to run local LLMs, hardware requirements depend on the specific model and your performance expectations. Lightweight models (such as Llama 2 7B or Phi-2) can run on modern CPUs with as little as 8 GB RAM, though performance may be limited. Larger models (like Llama 3 70B or Mixtral) typically require a dedicated GPU with at least 16 GB VRAM for efficient inference. &lt;/p&gt;

&lt;p&gt;Ollama supports both CPU and GPU execution. On CPU-only systems, you can expect slower response times, especially with larger models or concurrent requests. Using a compatible GPU significantly accelerates inference required for demanding workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Azure Cosmos DB
&lt;/h2&gt;

&lt;p&gt;Since you're working with local models, you'll likely want to use the Azure Cosmos DB emulator for local development. The emulator provides a local environment that mimics the Azure Cosmos DB service, enabling you to develop and test your applications without incurring costs or requiring an internet connection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Alternatively, you can use the cloud-based Azure Cosmos DB service. Simply &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/quickstart-portal" rel="noopener noreferrer"&gt;create an Azure Cosmos DB for NoSQL account&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/vector-search#enable-the-vector-indexing-and-search-feature" rel="noopener noreferrer"&gt;enable the vector search feature&lt;/a&gt;. Make sure to log in using &lt;code&gt;az cli&lt;/code&gt; with an identity that has RBAC permissions for the account, since the application uses &lt;code&gt;DefaultAzureCredential&lt;/code&gt; for authentication (not key-based authentication).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The emulator is available as a Docker container, which is the recommended way to run it. Here are the steps to pull and start the Cosmos DB emulator. The commands shown are for Linux - &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-develop-emulator?tabs=docker-linux%2Ccsharp&amp;amp;pivots=api-nosql#start-the-emulator" rel="noopener noreferrer"&gt;refer to the documentation&lt;/a&gt; for other platform options.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't have Docker installed, please refer to the &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker installation guide&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest

docker run &lt;span class="nt"&gt;--publish&lt;/span&gt; 8081:8081 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;AZURE_COSMOS_EMULATOR_PARTITION_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-develop-emulator?tabs=docker-linux%2Ccsharp&amp;amp;pivots=api-nosql#import-the-emulators-tlsssl-certificate" rel="noopener noreferrer"&gt;configure the emulator SSL certificate&lt;/a&gt;. For example, on the Linux system I was using, I ran the following commands to download the certificate and regenerate the certificate bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--insecure&lt;/span&gt; https://localhost:8081/_explorer/emulator.pem &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/emulatorcert.crt
&lt;span class="nb"&gt;sudo &lt;/span&gt;update-ca-certificates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Updating certificates in /etc/ssl/certs...
rehash: warning: skipping ca-certificates.crt,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Load data into Azure Cosmos DB
&lt;/h2&gt;

&lt;p&gt;Now that both Ollama and Azure Cosmos DB are set up, it's time to populate our vector database with some sample data. For this demonstration, we'll use Azure Cosmos DB's own documentation as our data source. The loader will fetch markdown content directly from the Microsoft Docs repository, specifically focusing on articles about Azure Cosmos DB &lt;a href="https://raw.githubusercontent.com/MicrosoftDocs/azure-databases-docs/refs/heads/main/articles/cosmos-db/nosql/vector-search.md" rel="noopener noreferrer"&gt;vector search&lt;/a&gt; functionality.&lt;/p&gt;

&lt;p&gt;Our data loading process will read these documentation articles, generate embeddings using the &lt;code&gt;mxbai-embed-large&lt;/code&gt; model, and store both the content and vector representations in Azure Cosmos DB for retrieval.&lt;/p&gt;

&lt;p&gt;Begin by cloning the GitHub repository containing the sample application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/abhirockzz/local-llms-rag-cosmosdb
&lt;span class="nb"&gt;cd &lt;/span&gt;local-llms-rag-cosmosdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running the loader application, ensure you have Python 3 installed on your system. Create a virtual environment and install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, configure the environment variables and execute the loading script. The example below uses the Azure Cosmos DB emulator for local development. If you prefer to use the cloud service instead, simply set the &lt;code&gt;COSMOS_DB_URL&lt;/code&gt; variable to your Azure Cosmos DB account URL and remove the &lt;code&gt;USE_EMULATOR&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# export COSMOS_DB_URL="https://&amp;lt;Cosmos DB account name&amp;gt;.documents.azure.com:443/"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;USE_EMULATOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DATABASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rag_local_llm_db"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"docs"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EMBEDDINGS_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mxbai-embed-large"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DIMENSIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1024"&lt;/span&gt;

python3 load_data.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script will automatically create the database and container if they don't already exist. Once the data loading process completes successfully, you should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uploading documents to Azure Cosmos DB ['https://raw.githubusercontent.com/MicrosoftDocs/azure-databases-docs/refs/heads/main/articles/cosmos-db/nosql/vector-search.md', 'https://raw.githubusercontent.com/MicrosoftDocs/azure-databases-docs/refs/heads/main/articles/cosmos-db/nosql/multi-tenancy-vector-search.md']
Using database: rag_local_llm_db, container: docs
Using embedding model: mxbai-embed-large with dimensions: 1024
Created instance of AzureCosmosDBNoSqlVectorSearch
Loading 26 document chunks from 2 documents
Data loaded into Azure Cosmos DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To confirm that your data has been loaded successfully, you can inspect the results using the Azure Cosmos DB Data Explorer. If you're using the emulator, navigate to &lt;code&gt;https://localhost:8081/_explorer/index.html&lt;/code&gt; in your browser. You should see the same number of documents in your container as the number of chunks reported by the loader application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run vector search queries
&lt;/h2&gt;

&lt;p&gt;Now that your data is loaded, let's test the vector search functionality. Set the same environment variables used for data loading and run the vector search script with your desired query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# export COSMOS_DB_URL="https://&amp;lt;Cosmos DB account name&amp;gt;.documents.azure.com:443/"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;USE_EMULATOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DATABASE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rag_local_llm_db"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"docs"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EMBEDDINGS_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mxbai-embed-large"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;DIMENSIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1024"&lt;/span&gt;

python3 vector_search.py &lt;span class="s2"&gt;"show me an example of a vector embedding policy"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script will process your query through the embedding model and perform a similarity search against the stored document vectors. You should see output similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Searching top 5 results for query: "show me an example of a vector embedding policy"

Using database: rag_local_llm_db, container: docs
Using embedding model: mxbai-embed-large with dimensions: 1024
Created instance of AzureCosmosDBNoSqlVectorSearch
Score: 0.7437641827298191
Content: ```



### A policy with two vector paths
//....


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The output shows the top five results ordered by their similarity scores, with higher scores indicating better matches to your query.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To modify the number of results returned, you can add the &lt;code&gt;top_k&lt;/code&gt; argument. For example, to retrieve the top 10 results, run: &lt;code&gt;python3 vector_search.py "show me an example of a vector embedding policy" 10&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Execute Retrieval-Augmented Generation (RAG) queries
&lt;/h2&gt;

&lt;p&gt;Now we will put it all together with an simple chat based interface that leverages the &lt;code&gt;llama3&lt;/code&gt; model to generate responses based on the contextual information retrieved from Azure Cosmos DB.&lt;/p&gt;

&lt;p&gt;Configure the environment variables needed for the RAG application and launch the script:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
# export COSMOS_DB_URL="https://&amp;lt;Cosmos DB account name&amp;gt;.documents.azure.com:443/"
export USE_EMULATOR="true"
export DATABASE_NAME="rag_local_llm_db"
export CONTAINER_NAME="docs"
export EMBEDDINGS_MODEL="mxbai-embed-large"
export DIMENSIONS="1024"
export CHAT_MODEL="llama3"

python3 rag_chain.py


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once the application initializes, you'll see output confirming the RAG chain setup:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
Building RAG chain. Using model: llama3
Using database: rag_local_llm_db, container: docs
Using embedding model: mxbai-embed-large with dimensions: 1024
Created instance of AzureCosmosDBNoSqlVectorSearch
Enter your questions below. Type 'exit' to quit, 'clear' to clear chat history, 'history' to view chat history.
[User]:


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Ask questions about the Azure Cosmos DB vector search documentation that you've loaded. For instance, try asking &lt;code&gt;show me an example of a vector embedding policy&lt;/code&gt;, and you'll see a response like this (note that these may vary slightly for your case, even across different runs):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
text
//...
[User]: show me an example of a vector embedding policy
[Assistant]: Here is an example of a vector embedding policy:

{
    "vectorEmbeddings": [
        {
            "path":"/vector1",
            "dataType":"float32",
            "distanceFunction":"cosine",
            "dimensions":1536
        },
        {
            "path":"/vector2",
            "dataType":"int8",
            "distanceFunction":"dotproduct",
            "dimensions":100
        }
    ]
}

This policy defines two vector embeddings: one with the path `/vector1`, using `float32` data type, cosine distance function, and having 1536 dimensions; and another with the path `/vector2`, using `int8` data type, dot product distance function, and having 100 dimensions.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To further explore the capabilities of your RAG system, try these additional example queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What is the maximum supported dimension for vector embeddings in Azure Cosmos DB?"&lt;/li&gt;
&lt;li&gt;"Is it suitable for large scale data?"&lt;/li&gt;
&lt;li&gt;"Is there a benefit to using the flat index type?"&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You can enter 'exit' to quit the application, 'clear' to clear chat history, or 'history' to view your previous interactions. Feel free to experiment with different data sources and queries. To modify the number of vector search results used as context, you can add the &lt;code&gt;TOP_K&lt;/code&gt; environment variable (defaults to 5).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;In this walkthrough, you followed step-by-step instructions to set up a complete RAG application that runs entirely on your local infrastructure — from installing and configuring Ollama with embedding and chat models, to setting up Azure Cosmos DB for vector storage, loading documentation data, and running using RAG through an interactive chat interface.&lt;/p&gt;

&lt;p&gt;Running models locally brings clear advantages in terms of costs, data privacy, and     connectivity constraints. However, you need to plan for appropriate hardware, particularly for larger models that perform best with dedicated GPUs and sufficient memory. The trade-off between model size, performance, and resource requirements is crucial when planning your local AI setup.&lt;/p&gt;

&lt;p&gt;Have you experimented with local LLMs in your projects? What challenges or benefits have you encountered when moving from cloud-based to local AI solutions? Perhaps you have used both approaches? Share your experience and feedback!&lt;/p&gt;

</description>
      <category>python</category>
      <category>langchain</category>
      <category>rag</category>
      <category>ai</category>
    </item>
    <item>
      <title>Integration testing for Go applications using Testcontainers and containerized databases</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Thu, 24 Jul 2025 05:43:01 +0000</pubDate>
      <link>https://dev.to/abhirockzz/integration-testing-for-go-applications-using-testcontainers-and-containerized-databases-3bfp</link>
      <guid>https://dev.to/abhirockzz/integration-testing-for-go-applications-using-testcontainers-and-containerized-databases-3bfp</guid>
      <description>&lt;p&gt;Integration testing has always presented a fundamental challenge: how do you test your application against real dependencies without the complexity of managing external services? Traditional approaches often involve either mocking dependencies (which can miss integration issues) or maintaining separate test environments (which can be expensive and difficult to manage consistently).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hello Testcontainers!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://testcontainers.com/" rel="noopener noreferrer"&gt;Testcontainers&lt;/a&gt; solves this problem elegantly by providing a way to run lightweight, throwaway instances of databases, message brokers, web servers, and other services directly within your test suite. Instead of complex setup scripts or shared test environments, you can spin up real services in Docker containers that exist only for the duration of your tests. The core value proposition is compelling: write tests that run against the actual technologies your application uses in production, while maintaining the isolation and repeatability that good tests require. When your tests complete, the containers are automatically cleaned up, leaving no trace behind.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://golang.testcontainers.org/" rel="noopener noreferrer"&gt;Testcontainers for Go&lt;/a&gt; (&lt;a href="https://pkg.go.dev/github.com/testcontainers/testcontainers-go" rel="noopener noreferrer"&gt;testcontainers-go&lt;/a&gt; package) brings this powerful testing approach to the Go ecosystem. It provides a clean, idiomatic Go API for creating and managing Docker containers within your test suite, handling the lifecycle management, port mapping, and health checks automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;p&gt;This post walks through a practical example of using &lt;code&gt;testcontainers-go&lt;/code&gt; with the Azure Cosmos DB emulator. Cosmos DB offers fully-featured Docker containers that mimic the behavior of the cloud service, making it an excellent candidate for integration testing with Testcontainers. Whether you're building applications that will eventually run against Azure Cosmos DB, or you're simply exploring document database patterns, the emulator provides a realistic testing environment without requiring cloud resources.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are two flavors of the Cosmos DB emulator. In this example, I have used the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux" rel="noopener noreferrer"&gt;Linux-based emulator (in preview)&lt;/a&gt; (which runs, almost anywhere, including ARM64 architecture). You are welcome to try out &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator?context=%2Fazure%2Fcosmos-db%2Fnosql%2Fcontext%2Fcontext" rel="noopener noreferrer"&gt;the other version&lt;/a&gt; as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'll examine a simple application with a focus on how &lt;code&gt;testcontainers&lt;/code&gt; handles the container setup, test execution, and cleanup. The app uses the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/sdk-go" rel="noopener noreferrer"&gt;Go SDK for Azure Cosmos DB&lt;/a&gt; (&lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos" rel="noopener noreferrer"&gt;azcosmos&lt;/a&gt; package) and exposes a simple REST API that manages items in a Cosmos DB container:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /items&lt;/code&gt; - Creates a new item with name, description, and category&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /items/{id}?category={category}&lt;/code&gt; - Retrieves an item by ID and category&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testcontainers and Cosmos DB emulator in action
&lt;/h2&gt;

&lt;p&gt;Let's see &lt;code&gt;testcontainers&lt;/code&gt; in action with a practical example. The tests demonstrate how &lt;code&gt;testcontainers&lt;/code&gt; automatically spins up a Cosmos DB emulator container, runs our integration tests against it, and cleans up afterward. This gives us the confidence of testing against real database behavior without the complexity of managing external services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the tests
&lt;/h3&gt;

&lt;p&gt;Before we examine the code, let's run the tests to see &lt;code&gt;testcontainers&lt;/code&gt; in action. Make sure you have &lt;a href="https://go.dev/doc/install" rel="noopener noreferrer"&gt;Go installed&lt;/a&gt; and Docker running on your machine. If you don't have Docker installed, you can follow the instructions on the &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pull the Cosmos DB emulator image from Docker Hub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, clone the repository and run the tests. The test suite will automatically start the Cosmos DB emulator in a Docker container, run the integration tests, and then clean up the container afterward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone github.com/abhirockzz/cosmosdb-testcontainers-go
&lt;span class="nb"&gt;cd &lt;/span&gt;cosmosdb-testcontainers-go

go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the tests passing with output similar to this (some lines removed for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;github.com/testcontainers/testcontainers-go - Connected to docker: 
  Server Version: 28.3.0
  API Version: 1.48
  Operating System: Docker Desktop
  Total Memory: 7836 MB
//.....
🐳 Creating container for image mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview
🐳 Creating container for image testcontainers/ryuk:0.11.0
✅ Container created: 5e43ec4bd8ea
🐳 Starting container: 5e43ec4bd8ea
✅ Container started: 5e43ec4bd8ea
⏳ Waiting for container id 5e43ec4bd8ea image: testcontainers/ryuk:0.11.0. Waiting for: &amp;amp;{Port:8080/tcp timeout:&amp;lt;nil&amp;gt; PollInterval:100ms skipInternalCheck:false}
🔔 Container is ready: 5e43ec4bd8ea
✅ Container created: cb1d2abb7255
🐳 Starting container: cb1d2abb7255
✅ Container started: cb1d2abb7255
⏳ Waiting for container id cb1d2abb7255 image: mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview. Waiting for: &amp;amp;{Port:8081 timeout:&amp;lt;nil&amp;gt; PollInterval:100ms skipInternalCheck:false}
🔔 Container is ready: cb1d2abb7255
=== RUN   TestCreateItem
--- PASS: TestCreateItem (0.00s)
=== RUN   TestCreateItem_FailureScenarios
//.....
--- PASS: TestCreateItem_FailureScenarios (0.00s)
    --- PASS: TestCreateItem_FailureScenarios/missing_name (0.00s)
    --- PASS: TestCreateItem_FailureScenarios/missing_category (0.00s)
    --- PASS: TestCreateItem_FailureScenarios/invalid_JSON (0.00s)
    --- PASS: TestCreateItem_FailureScenarios/wrong_HTTP_method (0.00s)
=== RUN   TestGetItem
--- PASS: TestGetItem (0.00s)
=== RUN   TestGetItem_FailureScenarios
//....
--- PASS: TestGetItem_FailureScenarios (0.00s)
    --- PASS: TestGetItem_FailureScenarios/missing_item_ID (0.00s)
    --- PASS: TestGetItem_FailureScenarios/missing_category_parameter (0.00s)
    --- PASS: TestGetItem_FailureScenarios/empty_category_parameter (0.00s)
    --- PASS: TestGetItem_FailureScenarios/non-existent_item_ID (0.00s)
    --- PASS: TestGetItem_FailureScenarios/valid_item_ID_but_wrong_category (0.00s)
PASS
🐳 Stopping container: cb1d2abb7255
✅ Container stopped: cb1d2abb7255
🐳 Terminating container: cb1d2abb7255
🚫 Container terminated: cb1d2abb7255
ok      demo    9.194s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the test flow
&lt;/h3&gt;

&lt;p&gt;The output shows how &lt;code&gt;testcontainers&lt;/code&gt; orchestrates the entire process in three distinct phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container Setup Phase: &lt;code&gt;testcontainers&lt;/code&gt; connects to Docker, starts the Cosmos DB emulator and Ryuk containers, and waits until the emulator is ready for use.&lt;/li&gt;
&lt;li&gt;Test Execution Phase: Integration tests run against the live Cosmos DB container, covering both successful operations and various error scenarios to ensure robust handling.&lt;/li&gt;
&lt;li&gt;Cleanup Phase: All containers are automatically stopped and removed, ensuring the system is left clean with no lingering resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The combination of &lt;code&gt;testcontainers&lt;/code&gt; and the Cosmos DB emulator gives you the reliability of testing against services without the operational overhead of managing test infrastructure. Every test run is isolated, repeatable, and starts from a known clean state. It gives you fast feedback while maintaining the confidence that comes from testing against actual database behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep dive
&lt;/h2&gt;

&lt;p&gt;Now let's examine how this works by walking through the code and exploring the key components that make this work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Orchestrating the test environment
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://pkg.go.dev/testing#hdr-Main" rel="noopener noreferrer"&gt;TestMain&lt;/a&gt; function serves as the entry point for our test suite, providing a way to perform setup and teardown operations that span multiple test functions. Unlike individual test functions that run in isolation, &lt;code&gt;TestMain&lt;/code&gt; runs once per package and gives you control over the entire test execution lifecycle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Set up the CosmosDB emulator container&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;emulator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setupCosmosDBEmulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to set up CosmosDB emulator: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// ... client setup and data seeding&lt;/span&gt;

    &lt;span class="c"&gt;// Run the tests&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Cleanup&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;emulator&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emulator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Terminate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure ensures that the expensive operation of starting a Docker container happens only once, while all individual tests can run against the same container instance. The pattern is particularly valuable when your setup involves external resources like databases or message brokers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the Cosmos DB emulator
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;setupCosmosDBEmulator&lt;/code&gt; function encapsulates the logic for creating and &lt;strong&gt;starting&lt;/strong&gt; the Cosmos DB emulator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;setupCosmosDBEmulator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;emulatorImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExposedPorts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;emulatorPort&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;":8081"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;WaitingFor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForListeningPort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emulatorPort&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"ENABLE_EXPLORER"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"PROTOCOL"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="s"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;testcontainers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GenericContainerRequest&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ContainerRequest&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Started&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// Give the emulator a bit more time to fully initialize&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ContainerRequest&lt;/code&gt; struct defines everything &lt;code&gt;testcontainers&lt;/code&gt; needs to know about our desired container. The &lt;code&gt;ExposedPorts&lt;/code&gt; field maps the container's internal port &lt;code&gt;8081&lt;/code&gt; to the same port on the host, while &lt;code&gt;WaitingFor&lt;/code&gt; ensures &lt;code&gt;testcontainers&lt;/code&gt; doesn't consider the container ready until the Cosmos DB service is actually listening on that port. The environment variables configure the emulator to run in HTTPS (&lt;code&gt;PROTOCOL&lt;/code&gt;) mode without the web-based data explorer (&lt;code&gt;ENABLE_EXPLORER&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;). I encourage you to explore the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux#docker-commands" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more configuration options that might suit your testing needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting to the emulator
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;auth.GetEmulatorClientWithAzureADAuth&lt;/code&gt; function is part of the &lt;a href="https://github.com/abhirockzz/cosmosdb-go-sdk-helper" rel="noopener noreferrer"&gt;cosmosdb-go-sdk-helper&lt;/a&gt; package. It provides a convenient way to create a Cosmos DB client that uses Microsoft Entra AD authentication, which is the recommended approach for production applications. However, in this test setup, &lt;code&gt;GetEmulatorClientWithAzureADAuth&lt;/code&gt; creates a &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos#ContainerClient" rel="noopener noreferrer"&gt;Cosmos DB client&lt;/a&gt; configured specifically for the emulator environment. By passing the custom transport through the &lt;code&gt;ClientOptions&lt;/code&gt;, we ensure that the SDK can successfully connect to the emulator despite its self-signed certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;TLSClientConfig&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InsecureSkipVerify&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}}&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetEmulatorClientWithAzureADAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emulatorEndpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that &lt;code&gt;InsecureSkipVerify: true&lt;/code&gt; should only be used in test/dev environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Test execution and cleanup
&lt;/h3&gt;

&lt;p&gt;After the container is ready and the client is configured, the code seeds test data through &lt;code&gt;seedTestData()&lt;/code&gt; to provide known items for the tests to work with. &lt;code&gt;m.Run()&lt;/code&gt; then executes all the actual test functions, returning an exit code that indicates whether the tests passed or failed.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;emulator.Terminate(ctx)&lt;/code&gt; call in the cleanup section ensures that &lt;code&gt;testcontainers&lt;/code&gt; properly stops and removes the Docker container, regardless of the test results. This cleanup is essential for preventing resource leaks and ensuring that subsequent test runs start with a clean slate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the right tools, the complexity of integration testing can be tamed. Testcontainers bridges this gap by making it easier to test against containerised services while maintaining isolation, repeatability, and eliminating the need for shared infrastructure.&lt;/p&gt;

&lt;p&gt;While this example focused on Azure Cosmos DB, the patterns apply broadly to any service that offers Docker containers, including PostgreSQL, Kafka, and more. Start with a single service and expand your test coverage gradually. &lt;/p&gt;

&lt;p&gt;Although the Cosmos DB emulator is a powerful tool for local development and testing, but it does not support every feature available in the full Azure Cosmos DB service. You can can read on the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator?context=%2Fazure%2Fcosmos-db%2Fnosql%2Fcontext%2Fcontext#differences-between-the-emulator-and-cloud-service" rel="noopener noreferrer"&gt;Differences between the emulator and cloud service&lt;/a&gt;, or consult the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/emulator-linux#feature-support" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; to understand the subset of features that are supported in the emulator.&lt;/p&gt;

&lt;p&gt;The full example code is available in the &lt;a href="https://github.com/abhirockzz/cosmosdb-testcontainers-go" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, and the &lt;a href="https://golang.testcontainers.org/" rel="noopener noreferrer"&gt;testcontainers-go documentation&lt;/a&gt; provides comprehensive guidance for exploring more. Happy building!&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>testing</category>
      <category>docker</category>
    </item>
    <item>
      <title>Building resilient Go applications: Simple tip for mocking and testing database error responses</title>
      <dc:creator>Abhishek Gupta</dc:creator>
      <pubDate>Sun, 06 Jul 2025 05:51:30 +0000</pubDate>
      <link>https://dev.to/abhirockzz/building-resilient-go-applications-simple-tip-for-mocking-and-testing-database-error-responses-4jgm</link>
      <guid>https://dev.to/abhirockzz/building-resilient-go-applications-simple-tip-for-mocking-and-testing-database-error-responses-4jgm</guid>
      <description>&lt;p&gt;When building applications that rely on databases (which is &lt;em&gt;almost&lt;/em&gt; every application, right?), one of the biggest challenges developers face is testing how their code handles various error scenarios. What happens when the database returns a &lt;code&gt;HTTP 400&lt;/code&gt; error? How does your application respond to throttling? Will your retry logic work as expected?&lt;/p&gt;

&lt;p&gt;These questions are crucial because in production, errors are inevitable. This holds true for Azure Cosmos DB as well. The database's distributed nature means that &lt;a href="https://learn.microsoft.com/en-us/rest/api/cosmos-db/http-status-codes-for-cosmosdb" rel="noopener noreferrer"&gt;errors can arise from various sources&lt;/a&gt;, including network issues (&lt;code&gt;503 Service Unavailable&lt;/code&gt;), request timeouts (&lt;code&gt;408 Request timeout&lt;/code&gt;), rate limits (&lt;code&gt;429 Too many requests&lt;/code&gt;), and more. Therefore, robust error handling and testing are essential to maintain a reliable application that handles these gracefully rather than crashing or losing data.&lt;/p&gt;

&lt;p&gt;Testing edge cases and different permutations of error scenarios traditionally requires a lot of effort and can be quite complex. Common approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Triggering real errors in your development environment (unreliable and hard to reproduce)&lt;/li&gt;
&lt;li&gt;Mocking entire SDK responses (complex and may not reflect real behavior)&lt;/li&gt;
&lt;li&gt;Waiting for errors to happen in production (definitely not ideal!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where error simulation can come in handy. By intercepting HTTP requests and selectively returning error responses, we can test specific scenarios in a controlled, repeatable manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulating errors using pluggable mechanisms
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to test error scenarios – from integration testing with real services to comprehensive mocking frameworks. This particular example uses the &lt;a href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/sdk-go" rel="noopener noreferrer"&gt;Go SDK for Azure Cosmos DB&lt;/a&gt; to illustrate how to simulate HTTP error conditions (&lt;code&gt;403 Forbidden&lt;/code&gt;) for a specific operation (such as &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos#ContainerClient.ReadItem" rel="noopener noreferrer"&gt;ReadItem&lt;/a&gt;). Note that this is just one technique in a broader toolkit of testing strategies.&lt;/p&gt;

&lt;p&gt;Follow the next steps if you want to run this as a standalone Go application and see how it works in practice. Or, feel free to skip to the next section for a walkthrough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Copy the code below into a file named &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/azcore"&lt;/span&gt;
    &lt;span class="n"&gt;azlog&lt;/span&gt; &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/azcore/log"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"&lt;/span&gt;
    &lt;span class="n"&gt;azruntime&lt;/span&gt; &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/azidentity"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;azlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="n"&gt;azlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c"&gt;// Log retry-related events&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;cls&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;azlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventRetryPolicy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Retry Policy Event: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="c"&gt;// Set logging level to include retries&lt;/span&gt;
    &lt;span class="n"&gt;azlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetEvents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;azlog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventRetryPolicy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// CustomTransport403Error implements policy.Transporter to simulate 403 errors only for ReadItem operations&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// Check if this is a ReadItem operation (typically a GET request with an item id in the path)&lt;/span&gt;
    &lt;span class="c"&gt;// ReadItem URLs look like: /dbs/{db}/colls/{container}/docs/{id}&lt;/span&gt;
    &lt;span class="n"&gt;isReadItemOperation&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/docs/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isReadItemOperation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CustomTransport403Error: Simulating 403 error for ReadItem operation: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="c"&gt;// Create a simulated 403 response with sub-status 3&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-activity-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"readitem-test-activity-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-request-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"readitem-test-request-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"403 Forbidden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NopCloser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`{"code": "Forbidden", "message": "Simulated 403 error for ReadItem with sub-status 3"}`&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Return both the response and the error so the SDK can handle it properly&lt;/span&gt;
        &lt;span class="n"&gt;responseErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azruntime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResponseError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseErr&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// For all other operations (like account properties), use a fake successful response&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CustomTransport403Error: Allowing operation: %s %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c"&gt;// Create a fake successful response for account properties and other operations&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-activity-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"success-activity-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-request-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"success-request-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"200 OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NopCloser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// RetryLoggingPolicy logs error details during retries&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// fmt.Println("RetryLoggingPolicy: Starting retry with request URL:", req.Raw().URL.String())&lt;/span&gt;

    &lt;span class="c"&gt;// Call the next policy in the chain&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// If there's an error, log the details&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"N/A"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RetryLoggingPolicy: ResponseError during retry - Status: %d, SubStatus: %s, URL: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RetryLoggingPolicy: Non-ResponseError during retry - %T: %v, URL: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Log HTTP error responses even if they don't result in Go errors&lt;/span&gt;
        &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"N/A"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RetryLoggingPolicy: HTTP error response - Status: %d, SubStatus: %s, URL: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;PerRetryPolicies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c"&gt;// This will log error details during retries&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c"&gt;// Use the selective transport to simulate 403 errors only for ReadItem&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azidentity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDefaultAzureCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://i_dont_exist.documents.azure.com:443"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;creds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NewClient Error occurred: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Test the ReadItem operation&lt;/span&gt;
    &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dummy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"dummy"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NewContainer Error occurred: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;partitionKey&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPartitionKeyString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"testpk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"testid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;handlerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handlerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ReadItem Error occurred"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Debug: Print the actual error type&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error type: %T&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// fmt.Printf("Error value: %v\n", err)&lt;/span&gt;

        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseError&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Successfully unwrapped to azcore.ResponseError using errors.As"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error status code: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"N/A"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error sub-status code: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Use the following commands to run the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod init demo
go mod tidy

go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets break this down and understand how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Custom transport layer for injecting errors
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CustomTransport403Error&lt;/code&gt; is a custom HTTP transport that intercepts requests before they reach Azure Cosmos DB. This transport examines each request and decides whether to simulate an error based on the operation type (e.g., &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos#ContainerClient.ReadItem" rel="noopener noreferrer"&gt;ReadItem&lt;/a&gt;). If the request matches the criteria, it returns a simulated error response; otherwise, it allows the request to proceed normally. &lt;/p&gt;

&lt;p&gt;It returns a &lt;code&gt;403 Forbidden&lt;/code&gt; HTTP response with a specific sub-status code (&lt;code&gt;3&lt;/code&gt; - &lt;code&gt;WriteForbidden&lt;/code&gt;), and its wrapped in an &lt;code&gt;azcore.ResponseError&lt;/code&gt; to closely mirror what Azure Cosmos DB would actually return. The &lt;code&gt;x-ms-substatus&lt;/code&gt; header is particularly important for Cosmos DB applications, as it provides specific context about why an operation failed. It's included to make sure that error handling code processes responses exactly as it would in production.&lt;/p&gt;

&lt;p&gt;You can customize the error simulation logic as needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;isReadItemOperation&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/docs/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isReadItemOperation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CustomTransport403Error: Simulating 403 error for ReadItem operation: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

        &lt;span class="c"&gt;// Create a simulated 403 response with sub-status 3&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-activity-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"readitem-test-activity-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-request-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"readitem-test-request-id"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="s"&gt;"403 Forbidden"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NopCloser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`{"code": "Forbidden", "message": "Simulated 403 error for ReadItem with sub-status 3"}`&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
            &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Return both the response and the error so the SDK can handle it properly&lt;/span&gt;
        &lt;span class="n"&gt;responseErr&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azruntime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResponseError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;responseErr&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// For all other operations, return a successful response&lt;/span&gt;
    &lt;span class="c"&gt;// ... (successful response creation code)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Go SDK for Azure Cosmos DB retries requests that return certain error codes, such as &lt;code&gt;403 (Forbidden)&lt;/code&gt;, and others. This custom transport allows you to simulate these conditions without needing to actually hit the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Observing SDK retries using custom policies
&lt;/h2&gt;

&lt;p&gt;I covered &lt;strong&gt;Retry Policies&lt;/strong&gt; in a previous blog post – &lt;a href="https://dev.to/abhirockzz/how-to-configure-and-customize-the-go-sdk-for-azure-cosmos-db-97a"&gt;How to configure and customize the Go SDK for Azure Cosmos DB&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In this example, a custom policy is used to provide visibility into the retry behavior. It logs detailed information about each retry attempt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Call the next policy in the chain&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// If there's an error, log the details&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"N/A"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RetryLoggingPolicy: ResponseError during retry - Status: %d, SubStatus: %s, URL: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is really useful for understanding how your application behaves under error conditions. When applications encounter errors, the SDK &lt;strong&gt;automatically&lt;/strong&gt; retries those requests. They might ultimately succeed after a few attempts, but you may want to have visibility into this process.&lt;/p&gt;

&lt;p&gt;You can plug in a custom &lt;code&gt;RetryLoggingPolicy&lt;/code&gt; to intercept these retries and log relevant information, such as the status code, sub-status code, and the URL of the request. This helps you understand how your application behaves during error conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Integration and error handling verification
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; function ties everything together. It sets up the Cosmos DB client with the custom transport and retry policy, then performs a &lt;code&gt;ReadItem&lt;/code&gt; operation. If an error occurs, it uses the &lt;code&gt;handlerError&lt;/code&gt; function to extract and log the status code and sub-status code from the error response.&lt;/p&gt;

&lt;p&gt;The custom transport and retry policy are integrated into the application as part of &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos#ClientOptions" rel="noopener noreferrer"&gt;ClientOptions&lt;/a&gt;. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The custom transport is configured as the &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy#Transporter" rel="noopener noreferrer"&gt;Transporter&lt;/a&gt; which represents an HTTP pipeline transport used to send HTTP requests and receive responses.&lt;/li&gt;
&lt;li&gt;The retry policy is added to the &lt;a href="https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azcore/policy#ClientOptions.PerRetryPolicies" rel="noopener noreferrer"&gt;PerRetryPolicies&lt;/a&gt; list. Each policy is executed once per request, and for each retry of that request.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azcosmos&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;PerRetryPolicies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;RetryLoggingPolicy&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c"&gt;// This will log error details during retries&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;CustomTransport403Error&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c"&gt;// Use the custom transport&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// ... client creation and ReadItem operation ...&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;partitionKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"testid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;handlerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handlerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;azcore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error status code: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;azErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ms-substatus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;subStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"N/A"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error sub-status code: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subStatus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By making error scenarios easy to reproduce and test, you're more likely to build applications that handle them gracefully. This pattern can be extended to test various scenarios such as different HTTP status codes (&lt;code&gt;429&lt;/code&gt; for throttling, for example), network timeouts, intermittent failures that succeed after retries, circuit breaker patterns, fallback mechanisms, and more.&lt;/p&gt;

&lt;p&gt;To begin with, you can focus on combination specific operations (like &lt;code&gt;read&lt;/code&gt;, or &lt;code&gt;write&lt;/code&gt;) and error types that are most relevant to your application. You can gradually expand this to cover more complex scenarios, such as simulating throttling, server errors, or even network timeouts.&lt;/p&gt;

</description>
      <category>go</category>
      <category>database</category>
      <category>cloud</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
