Configuration
Cratis Arc can be configured both through appsettings.json and programmatically to customize its behavior. The main configuration is handled through the ArcOptions class.
This page covers the ASP.NET Core host specifically. Code blocks are configuration/service fragments for an existing Cratis.Arc web project; import Cratis.Arc, System.Text.Json, and Microsoft.Extensions.Options as used. Types such as MyCustomIdentityDetailsProvider and MyCustomConverter are application-owned examples, not built-in Arc types. For the lightweight Arc.Core host and its listen URL configuration, see Arc.Core Getting Started.
Default Configuration Section
Section titled “Default Configuration Section”By default, Arc looks for configuration under the Cratis:Arc section in your appsettings.json file. The same keys can be supplied through environment variables — .NET maps the __ separator onto nested keys, so Cratis:Arc:GeneratedApis:RoutePrefix becomes Cratis__Arc__GeneratedApis__RoutePrefix.
Configuration Options
Section titled “Configuration Options”Configuration example
Section titled “Configuration example”Here’s an example covering the most common options, bound from appsettings.json under Cratis:Arc. The complete options tree also documents ExposeExceptionDetails, Tenancy.BaseDomain, and host-only settings.
{ "Cratis": { "Arc": { "CorrelationId": { "HttpHeader": "X-Correlation-ID" }, "Tenancy": { "ResolverType": "Header", "HttpHeader": "x-cratis-tenant-id" }, "GeneratedApis": { "RoutePrefix": "api", "SegmentsToSkipForRoute": 0, "IncludeCommandNameInRoute": true, "IncludeQueryNameInRoute": true }, "Query": { "KeepAliveInterval": "00:00:30" } } }}Configuration Properties
Section titled “Configuration Properties”CorrelationId
Section titled “CorrelationId”Controls how correlation IDs are handled in HTTP requests.
- HttpHeader (string, default:
"X-Correlation-ID"): The HTTP header name to use for correlation ID tracking.
Tenancy
Section titled “Tenancy”Controls how the active tenant is resolved on each request.
- ResolverType (
TenantResolverType, default:Header): How to resolve the tenant —Header,Query,Claim,Subdomain,Development, orFixed. - HttpHeader (string, default:
"x-cratis-tenant-id"): The HTTP header used whenResolverTypeisHeader, and the fallback header when it isSubdomain. - QueryParameter (string, default:
"tenantId"): The query-string parameter used whenResolverTypeisQuery. - ClaimType (string, default:
"tenant_id"): The claim used whenResolverTypeisClaim. - FixedTenantId (string, default:
"development"): The tenant every request resolves to whenResolverTypeisFixedorDevelopment. PreferFixedfor a single-tenant production deployment —Developmentresolves identically but is named for an environment it does not check. - DevelopmentTenantId (string, default:
"development"): The same value under its original name — reading or writing either key sets both. Supply only one; if both are present the binder’s property order decides.
GeneratedApis
Section titled “GeneratedApis”Controls how automatically generated API endpoints are configured for commands and queries.
- RoutePrefix (string, default:
"api"): The base route prefix for all generated API endpoints. - SegmentsToSkipForRoute (int, default:
0): Number of namespace segments to skip when constructing routes from type namespaces. - IncludeCommandNameInRoute (bool, default:
true): Whether to include the command type name as the last segment of the route for command endpoints. - IncludeQueryNameInRoute (bool, default:
true): Whether to include the query name as the last segment of the route for query endpoints. - EnableQueryHttpMethod (bool, default:
true): Whether generated queries also accept HTTPQUERYwith arguments in a JSON body. GET remains available. See HTTP QUERY.
Controls observable (real-time) queries.
- KeepAliveInterval (
TimeSpan, default:00:00:30): How often a keep-alive frame is sent on an open observable-query connection.
IdentityDetailsProvider
Section titled “IdentityDetailsProvider”- IdentityDetailsProvider (Type, default:
null): Specifies a custom identity details provider type. If not specified, the system will use type discovery to find one automatically.
Setup Methods
Section titled “Setup Methods”The ASP.NET Core host bootstraps with WebApplication.CreateBuilder, registers Arc with AddCratisArc, and activates it with UseCratisArc.
Using Configuration File
Section titled “Using Configuration File”The most common approach is to use the configuration file with the default section:
var builder = WebApplication.CreateBuilder(args);
// Bind the default configuration section (Cratis:Arc)builder.AddCratisArc();
var app = builder.Build();app.UseCratisArc();app.Run();Using Custom Configuration Section
Section titled “Using Custom Configuration Section”You can specify a custom configuration section path:
var builder = WebApplication.CreateBuilder(args);
// Bind a custom configuration sectionbuilder.AddCratisArc(configSectionPath: "MyApp:CratisConfig");
var app = builder.Build();app.UseCratisArc();app.Run();Programmatic Configuration
Section titled “Programmatic Configuration”You can configure the options through code with the configureOptions callback:
var builder = WebApplication.CreateBuilder(args);
builder.AddCratisArc(options =>{ // Configure correlation ID options.CorrelationId.HttpHeader = "X-My-Correlation-ID";
// Configure tenancy options.Tenancy.HttpHeader = "X-Custom-Tenant";
// Configure generated APIs options.GeneratedApis.RoutePrefix = "myapi"; options.GeneratedApis.SegmentsToSkipForRoute = 2; options.GeneratedApis.IncludeCommandNameInRoute = false; options.GeneratedApis.IncludeQueryNameInRoute = false;
// Set a custom identity details provider options.IdentityDetailsProvider = typeof(MyCustomIdentityDetailsProvider);});
var app = builder.Build();app.UseCratisArc();app.Run();Hybrid Configuration
Section titled “Hybrid Configuration”AddCratisArc binds appsettings.json first and then applies the configureOptions callback, so the callback acts as a programmatic override on top of file-based settings:
var builder = WebApplication.CreateBuilder(args);
// Values come from Cratis:Arc; the callback overrides specific settingsbuilder.AddCratisArc(options =>{ options.GeneratedApis.RoutePrefix = "v1/api";});
var app = builder.Build();app.UseCratisArc();app.Run();Environment-Specific Configuration
Section titled “Environment-Specific Configuration”You can use different configurations for different environments using the standard ASP.NET Core configuration pattern:
appsettings.json (base configuration):
{ "Cratis": { "Arc": { "GeneratedApis": { "RoutePrefix": "api" } } }}appsettings.Development.json (development overrides):
{ "Cratis": { "Arc": { "CorrelationId": { "HttpHeader": "X-Dev-Correlation-ID" } } }}appsettings.Production.json (production overrides):
{ "Cratis": { "Arc": { "GeneratedApis": { "RoutePrefix": "v1" } } }}Route Generation Examples
Section titled “Route Generation Examples”The GeneratedApis configuration affects how routes are generated for your commands and queries. Here are some examples:
Given a command class MyApp.Sales.Commands.CreateOrderCommand:
Default Configuration
Section titled “Default Configuration”{ "GeneratedApis": { "RoutePrefix": "api", "SegmentsToSkipForRoute": 0, "IncludeCommandNameInRoute": true, "IncludeQueryNameInRoute": true }}Generated route: /api/my-app/sales/commands/create-order-command
Skip Namespace Segments
Section titled “Skip Namespace Segments”{ "GeneratedApis": { "RoutePrefix": "api", "SegmentsToSkipForRoute": 2, "IncludeCommandNameInRoute": true, "IncludeQueryNameInRoute": true }}Generated route: /api/commands/create-order-command (skips MyApp and Sales).
Exclude Type Names
Section titled “Exclude Type Names”{ "GeneratedApis": { "RoutePrefix": "api", "SegmentsToSkipForRoute": 2, "IncludeCommandNameInRoute": false, "IncludeQueryNameInRoute": false }}Generated route: /api/commands for this single command. A single query with location MyApp.Sales.Queries would use /api/queries with these options.
For example, with the configuration above:
- If you have only
MyApp.Sales.Commands.CreateOrder, the route will be/api/commands - If you have both
MyApp.Sales.Commands.CreateOrderandMyApp.Sales.Commands.UpdateOrder, the routes will be/api/commands/create-orderand/api/commands/update-orderrespectively (type names added automatically to avoid conflict)
JSON Serialization
Section titled “JSON Serialization”Arc-generated endpoints use ArcOptions.JsonSerializerOptions. Arc’s MVC post-configuration copies only the property naming policy and appends Arc converters; it does not copy the entire options object. Settings such as DefaultIgnoreCondition, NumberHandling, and other MVC serializer options require separate MVC configuration if controller output must match. Manual serialization must explicitly use the Arc options; unrelated minimal API endpoints use their ASP.NET serializer configuration.
Default Configuration
Section titled “Default Configuration”Arc’s own JsonSerializerOptions has the following defaults (not all are copied to MVC):
- Property Naming: Camel case with acronym-friendly handling (e.g.,
XMLParserbecomesxmlParser) - Null Handling: Null values are ignored when writing JSON
- Enums: Serialized as integers (not strings)
- Concepts: Full support for Cratis Concepts (strongly-typed primitives)
- Date/Time: Support for
DateOnlyandTimeOnlytypes - Types: Support for
System.TypeandSystem.Uriserialization - Derived Types: Polymorphic serialization support when derived types are discovered
Accessing JsonSerializerOptions
Section titled “Accessing JsonSerializerOptions”The configured JsonSerializerOptions is available through dependency injection:
using System.Text.Json;
public class MessageSerializer(JsonSerializerOptions jsonOptions){ public string Serialize(string message) => JsonSerializer.Serialize(new { Message = message }, jsonOptions);}Or through ArcOptions:
public class MyService{ public MyService(IOptions<ArcOptions> arcOptions) { var jsonOptions = arcOptions.Value.JsonSerializerOptions; }}Customizing JSON Serialization
Section titled “Customizing JSON Serialization”You can add custom converters or modify the configuration through the options pattern:
builder.AddCratisArc(options =>{ // Add a custom converter options.JsonSerializerOptions.Converters.Add(new MyCustomConverter());});For controllers, configure null omission, number handling, and other non-copied settings separately through MVC’s AddJsonOptions. Arc’s post-configuration still sets the naming policy, removes the first JsonStringEnumConverter if present, and appends Arc converters. ConfigureHttpJsonOptions configures ASP.NET minimal API serialization; it does not replace Arc’s serializer for generated endpoints. Converter order matters: the first matching converter wins, so appending a converter may not override an existing Arc converter. Test the actual request/response contract; see OpenAPI enum limitations.
Best Practices
Section titled “Best Practices”-
Use Configuration Files: For most scenarios, use
appsettings.jsonconfiguration as it allows easy environment-specific overrides without code changes. -
Environment-Specific Settings: Use
appsettings.{Environment}.jsonfiles for environment-specific configurations. -
Programmatic Configuration: Use programmatic configuration when you need to:
- Set configuration based on runtime conditions
- Use custom identity providers
- Override specific settings that can’t be easily expressed in JSON
-
Route Planning: Consider your API route structure carefully when configuring
GeneratedApisoptions, especially in public-facing APIs where route stability is important. -
Header Standardization: Use standard HTTP header names for correlation IDs and tenant IDs that align with your organization’s conventions and any API gateways or load balancers in use.
-
JSON Consistency: Use the injected
JsonSerializerOptionsfor manual serialization that must match generated Arc endpoints. Configure and test MVC’s remaining options separately when controller output must match too.