Industry Insights, Information, and Developer News
I just wanted to get my app to talk to the Cosmos DB emulator. That's it. Just run my app locally against the emulator and see if my code worked.
But first, I had to configure 12 things:
The solution was the Builder Pattern, and I'm going to walk you through how to implement it.
The Twelve-Parameter Constructor Twelve configuration values before I could say hello. And here's the thing—for the emulator, most of those values are always the same. The endpoint is always https://localhost:8081/. The account key is always that same well-known key that everyone Googles. You have to use Gateway mode. Bulk execution doesn't work. You obviously want it to create structures for you because it's local dev.
But there I was, specifying all twelve values. Every. Single. Time.
Here's what my CosmosConfig class looks like. It's a straightforward configuration object with a bunch of properties:
public class CosmosConfig { public string AccountKey { get; set; } public string Endpoint { get; set; } public string DatabaseName { get; set; } public string ContainerName { get; set; } public string PartitionKey { get; set; } public bool CreateStructures { get; set; } public bool UseGatewayMode { get; set; } public bool UseDefaultAzureCredential { get; set; } public bool UseHierarchicalPartitionKey{ get; set; } public int DatabaseThroughput { get; set; } public bool AllowBulkExecution { get; set; } public bool UseCamelCase { get; set; } }
In an ASP.NET Core app, you can bind this from appsettings.json using IConfiguration and life is fine. But what about a console app? A .NET tool? A unit test? An integration test? Anywhere you don't have the ASP.NET Core configuration system doing the heavy lifting, you end up writing code like this:
var config = new CosmosConfig(); config.Endpoint = "https://localhost:8081/"; config.AccountKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="; config.DatabaseName = "myapp"; config.ContainerName = "items"; config.PartitionKey = "/ownerId"; config.CreateStructures = true; config.UseGatewayMode = true; config.AllowBulkExecution = false; config.UseHierarchicalPartitionKey = false; config.UseDefaultAzureCredential = false; config.DatabaseThroughput = 400; config.UseCamelCase = true;
That's thirteen lines just to say "connect to the local emulator." And buried in there is tribal knowledge that you had to learn the hard way—like the fact that the beta version of the Linux-based emulator requires Gateway mode, or that bulk execution silently doesn't work against the emulator. Every new developer on the team is going to get at least three of these wrong. I know because I got them wrong. (I'm wickid smaht.)
Enter the Builder The Builder Pattern gives you a fluent API for constructing complex objects. Instead of setting properties one at a time, you chain method calls that read like a sentence.
Here's the same "connect to the emulator" scenario using a builder:
var config = new CosmosConfigBuilder() .ForEmulator() .WithDatabase("myapp") .WithContainer("items") .Build();
Four lines. All of that "tribal knowledge" is baked into the ForEmulator() method. For production, it stays clean:
var config = new CosmosConfigBuilder() .WithEndpoint("https://myaccount.documents.azure.com:443/") .UseDefaultAzureCredential() .WithDatabase("production-db") .WithContainer("app-data") .Build();
How It Works The implementation is surprisingly simple. The builder holds a private instance of the object and each fluent method returns this:
public class CosmosConfigBuilder { private readonly CosmosConfig _config; public CosmosConfigBuilder() { _config = new CosmosConfig { PartitionKey = CosmosDbConstants.DefaultPartitionKey }; } public CosmosConfigBuilder WithEndpoint(string endpoint) { _config.Endpoint = endpoint; return this; } public CosmosConfigBuilder WithDatabase(string databaseName, int? throughput = null) { _config.DatabaseName = databaseName; if (throughput.HasValue) { _config.DatabaseThroughput = throughput.Value; } return this; } public CosmosConfig Build() { ValidateConfiguration(); return _config; } }
Encoding Knowledge Into Methods The ForEmulator() method is where the real power lies. It's just calling other builder methods, but it encapsulates the specific requirements of the environment:
public CosmosConfigBuilder ForEmulator() { return WithEndpoint("https://localhost:8081/") .WithAccountKey("C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==") .UseGatewayMode() .WithBulkExecution(false) .WithCreateStructures(); }
Validation at Build Time The Build() method gives you a centralized place to validate. By throwing an exception here, you prevent the app from failing deep inside the Cosmos SDK with a cryptic error.
private void ValidateConfiguration() { if (string.IsNullOrWhiteSpace(_config.Endpoint)) { throw new InvalidOperationException("Endpoint must be configured. Use WithEndpoint() to set it."); } // ... more validation ... }
You've Already Used This Pattern The .NET ecosystem is full of builders:
The Code This builder is part of my open-source Benday.CosmosDb library. It's available on NuGet:
dotnet add package Benday.CosmosDb
If you're working with Azure Cosmos DB from C#, the library handles a lot more than just configuration — repositories, owned-item patterns for multi-tenancy, RU logging, and batch utilities. But the builder is probably my favorite part because it turned "twelve things I have to remember" into "four lines I can actually read."
About the Author
Posted by Benjamin Day on 03/30/2026