Using effectivelly appsettings.json in .NET: A Strongly-Typed Approach for Modern Applications
All configurations in one file is all you need
In several projects I’ve worked on over the years, I’ve inevitably come across inappropriate use of appsettings.json in .NET C# applications. But it’s normal, beginners end up getting confused because there are appsettings.json, launchSettings.json, etc.
If you are interested in more content like this one, consider get in Averin Community on Discord by click here
As software engineers, we have a lot of configurations to do as we’re developing and deploying out application in multiples environment: Stage, Production, Development etc. From cloud services (Azure, AWS, GCP) and databases (SQL Server, MySQL, PostgreSQL) to caching layers (Redis), message brokers (RabbitMQ, Kafka), and observability tools (Jaeger, Prometheus, Grafana Loki, Elasticsearch), our applications are sophisticated mosaics of interconnected services.
Each piece of this mosaic requires its own set of configurations: connection strings, URLs, cloud regions, topic names, bucket names, and API keys. Traditionally, managing these settings in .NET often involves scattering calls to IConfiguration throughout our codebase and that it’s bad we already know:
This "magic string" approach works, but it's fragile! A simple typo can lead to runtime errors that are hard to debug. And what is hard to debug is take a long time! Refactoring becomes a risky "find and replace" operation, and there's no IntelliSense to guide us. We even have AIs, but it's not so deterministic.
What if we could do better? What if we could treat our configuration as a first-class citizen in our code, with all the benefits of static typing? Let's explore a robust and scalable pattern for managing configuration in any modern .NET application.
Who are using it? A large open-source software project called Bitwarden, a password manager, uses this standard. I haven't come up with a name for it yet, but I'll call it GlobalSettings. I like the name and think it captures the nature of our complex configurations well. So, if you're as curious as I am, here's the link to their core project, which uses this standard.
At the end of this post, I will make available a project from my github that makes this use practical with many configurations.
The Core Idea: A Centralized GlobalSettings Class
The principle is simple: we'll create a single, overarching class, let's call it GlobalSettings, that will serve as the container for all our application's configuration. This class will be composed of more specific setting objects, each tailored to a particular service.
To promote good design and testability, we'll define interfaces for our settings. Let's start with the root objects:
C#
By defining IGlobalSettings, we can easily mock our configuration in unit tests, swapping out real connection strings for in-memory or test-specific ones.
Composing Our Settings: The Database Example
Now, let's look at a common case: database connection strings. Both MySQL and SQL Server are relational SQL databases, so they can share a common interface for their settings.
Here’s a refined version of the example for SQL settings.
C#
And the implementation. This class can contain not just the strings, but also related logic or properties.
C#
Notice the powerful fallback logic in the ReadOnlyConnectionString property. This is the kind of business logic that is impossible to represent with simple key-value pairs but fits perfectly within a C# class.
Binding Configuration with appsettings.json
This C# structure maps directly to a clean, hierarchical structure in our appsettings.json file. The .NET configuration binder will automatically hydrate our C# objects from this JSON.
Here’s how the corresponding appsettings.json would look:
JSON
The section name GlobalSettings in the JSON maps directly to our root class. The nested objects SqlServer and MySql map to the properties within GlobalSettings.
Wiring It Up in a .NET Web API
The final piece of the puzzle is to teach our application how to load this configuration and make it available through Dependency Injection (DI). In a modern .NET API, this is done elegantly in Program.cs.
C#
With just two lines of code, we have:
Bound the
"GlobalSettings"section fromappsettings.jsonto ourGlobalSettingsclass.Registered
IGlobalSettingsin the DI container as a singleton, ensuring that the same configuration instance is used throughout the application's lifetime.
Using Your Typed Configuration
Now, using the configuration in a controller or service is clean, safe, and intuitive.
C#
The benefits are immediate:
Discoverability: IntelliSense shows you all available settings.
Type Safety: The compiler ensures you're using the right types.
Refactorability: Renaming a property in your C# class is a safe, IDE-assisted operation.
Single Source of Truth: All configuration logic is centralized.
Your Turn: Expand the Pattern
This pattern is a foundation. I encourage you to expand it to fit your needs.
Cloud Services: Add settings for Azure Blob Storage or AWS S3.
Message Brokers: Configure Kafka or RabbitMQ.
C#
Observability: Manage settings for Jaeger or Prometheus.
C#
The corresponding appsettings.json would simply grow to include these new sections under GlobalSettings.
By embracing this strongly-typed configuration pattern, you can build more robust, maintainable, and developer-friendly .NET applications. Give it a try in your next project—your future self will thank you.












