Consuming application configuration is a common task when writing applications. Frequently, configuration stores are used to manage this configuration data. A configuration item is often dynamic in nature and tightly coupled to the needs of the application that consumes it.
For example, application configuration can include:
Names of secrets
Different identifiers
Partition or consumer IDs
Names of databases to connect to, etc
Usually, configuration items are stored as key/value items in a state store or database. Developers or operators can change application configuration at runtime in the configuration store. Once changes are made, a service is notified to load the new configuration.
Configuration data is read-only from the application API perspective, with updates to the configuration store made through operator tooling. With Dapr’s configuration API, you can:
Consume configuration items that are returned as read-only key/value pairs
Subscribe to changes whenever a configuration item changes
Note
The Configuration API should not be confused with the Dapr sidecar and control plane configuration, which is used to set policies and settings on Dapr sidecar instances or the installed Dapr control plane.
Try out configuration
Quickstart
Want to put the Dapr configuration API to the test? Walk through the following quickstart to see the configuration API in action:
Get configuration items or subscribe to configuration changes using the configuration API.
Start using the configuration API directly in your app
Want to skip the quickstarts? Not a problem. You can try out the configuration building block directly in your application to read and manage configuration data. After Dapr is installed, you can begin using the configuration API starting with the configuration how-to guide.
Create a configuration item in a supported configuration store. This can be a simple key-value item, with any key of your choice. As mentioned earlier, this example uses the Redis configuration store component.
Run Redis with Docker
docker run --name my-redis -p 6379:6379 -d redis:6
Save an item
Using the Redis CLI, connect to the Redis instance:
redis-cli -p 6379
Save a configuration item:
MSET orderId1 "101||1" orderId2 "102||1"
Configure a Dapr configuration store
Save the following component file to the default components folder on your machine. You can use this as the Dapr component YAML:
For Kubernetes using kubectl.
When running with the Dapr CLI.
Note
Since the Redis configuration component has identical metadata to the Redis statestore.yaml component, you can simply copy/change the Redis state store component type if you already have a Redis statestore.yaml.
#dependenciesfromdapr.clientsimportDaprClient#codewithDaprClient()asd:CONFIG_STORE_NAME='configstore'keys=['orderId1','orderId2']#Startup time for daprd.wait(20)configuration=d.get_configuration(store_name=CONFIG_STORE_NAME,keys=[keys],config_metadata={})print(f"Got key={configuration.items[0].key} value={configuration.items[0].value} version={configuration.items[0].version}")
packagemainimport("context""fmt"dapr"github.com/dapr/go-sdk/client")funcmain(){ctx:=context.Background()client,err:=dapr.NewClient()iferr!=nil{panic(err)}items,err:=client.GetConfigurationItems(ctx,"configstore",["orderId1","orderId2"])iferr!=nil{panic(err)}forkey,item:=rangeitems{fmt.Printf("get config: key = %s value = %s version = %s",key,(*item).Value,(*item).Version)}}
import{CommunicationProtocolEnum,DaprClient}from"@dapr/dapr";// JS SDK does not support Configuration API over HTTP protocol yet
constprotocol=CommunicationProtocolEnum.GRPC;consthost=process.env.DAPR_HOST??"localhost";constport=process.env.DAPR_GRPC_PORT??3500;constDAPR_CONFIGURATION_STORE="configstore";constCONFIGURATION_ITEMS=["orderId1","orderId2"];asyncfunctionmain(){constclient=newDaprClient(host,port,protocol);// Get config items from the config store
try{constconfig=awaitclient.configuration.get(DAPR_CONFIGURATION_STORE,CONFIGURATION_ITEMS);Object.keys(config.items).forEach((key)=>{console.log("Configuration for "+key+":",JSON.stringify(config.items[key]));});}catch(error){console.log("Could not get config item, err:"+error);process.exit(1);}}main().catch((e)=>console.error(e));
Launch a dapr sidecar:
dapr run --app-id orderprocessing --dapr-http-port 3601
In a separate terminal, get the configuration item saved earlier:
Below are code examples that leverage SDKs to subscribe to keys [orderId1, orderId2] using configstore store component.
usingSystem;usingSystem.Collections.Generic;usingSystem.Threading.Tasks;usingDapr.Client;usingSystem.Text.Json;conststringDAPR_CONFIGURATION_STORE="configstore";varCONFIGURATION_ITEMS=newList<string>{"orderId1","orderId2"};varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddDaprClient();varapp=builder.Build();varclient=app.Services.GetRequiredService<DaprClient>();// Subscribe for configuration changesvarsubscribe=awaitclient.SubscribeConfiguration(DAPR_CONFIGURATION_STORE,CONFIGURATION_ITEMS);// Print configuration changesawaitforeach(varitemsinsubscribe.Source){// First invocation when app subscribes to config changes only returns subscription idif(items.Keys.Count==0){Console.WriteLine("App subscribed to config changes with subscription id: "+subscribe.Id);subscriptionId=subscribe.Id;continue;}varcfg=JsonSerializer.Serialize(items);Console.WriteLine("Configuration update "+cfg);}
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
dapr run --app-id orderprocessing -- dotnet run
usingSystem;usingMicrosoft.AspNetCore.Hosting;usingMicrosoft.Extensions.Hosting;usingDapr.Client;usingDapr.Extensions.Configuration;usingSystem.Collections.Generic;usingSystem.Threading;Console.WriteLine("Starting application.");varbuilder=WebApplication.CreateBuilder(args);// Unlike most other situations, we build a `DaprClient` here using its factory because we cannot rely on `IConfiguration`// or other injected services to configure it because we haven't yet built the DI container.varclient=newDaprClientBuilder().Build();// In a real-world application, you'd also add the following line to register the `DaprClient` with the DI container so// it can be injected into other services. In this demonstration, it's not necessary as we're not injecting it anywhere. // builder.Services.AddDaprClient();// Get the initial value and continue to watch it for changes builder.Configuration.AddDaprConfigurationStore("configstore",newList<string>(){"orderId1","orderId2"},client,TimeSpan.FromSeconds(20));builder.Configuration.AddStreamingDaprConfigurationStore("configstore",newList<string>(){"orderId1","orderId2"},client,TimeSpan.FromSeconds(20));awaitbuilder.Build().RunAsync();Console.WriteLine("Closing application.");
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
dapr run --app-id orderprocessing -- dotnet run
importio.dapr.client.DaprClientBuilder;importio.dapr.client.DaprClient;importio.dapr.client.domain.ConfigurationItem;importio.dapr.client.domain.GetConfigurationRequest;importio.dapr.client.domain.SubscribeConfigurationRequest;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;//codeprivatestaticfinalStringCONFIG_STORE_NAME="configstore";privatestaticStringsubscriptionId=null;publicstaticvoidmain(String[]args)throwsException{try(DaprClientclient=(newDaprClientBuilder()).build()){// Subscribe for config changesList<String>keys=newArrayList<>();keys.add("orderId1");keys.add("orderId2");Flux<SubscribeConfigurationResponse>subscription=client.subscribeConfiguration(DAPR_CONFIGURATON_STORE,keys);// Read config changes for 20 secondssubscription.subscribe((response)->{// First ever response contains the subscription idif(response.getItems()==null||response.getItems().isEmpty()){subscriptionId=response.getSubscriptionId();System.out.println("App subscribed to config changes with subscription id: "+subscriptionId);}else{response.getItems().forEach((k,v)->{System.out.println("Configuration update for "+k+": {'value':'"+v.getValue()+"'}");});}});Thread.sleep(20000);}}
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
dapr run --app-id orderprocessing -- -- mvn spring-boot:run
#dependenciesfromdapr.clientsimportDaprClient#codedefhandler(id:str,resp:ConfigurationResponse):forkeyinresp.items:print(f"Subscribed item received key={key} value={resp.items[key].value} "f"version={resp.items[key].version} "f"metadata={resp.items[key].metadata}",flush=True)defexecuteConfiguration():withDaprClient()asd:storeName='configurationstore'keys=['orderId1','orderId2']id=d.subscribe_configuration(store_name=storeName,keys=keys,handler=handler,config_metadata={})print("Subscription ID is",id,flush=True)sleep(20)executeConfiguration()
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
dapr run --app-id orderprocessing -- python3 OrderProcessingService.py
packagemainimport("context""fmt""time"dapr"github.com/dapr/go-sdk/client")funcmain(){ctx:=context.Background()client,err:=dapr.NewClient()iferr!=nil{panic(err)}subscribeID,err:=client.SubscribeConfigurationItems(ctx,"configstore",[]string{"orderId1","orderId2"},func(idstring,itemsmap[string]*dapr.ConfigurationItem){fork,v:=rangeitems{fmt.Printf("get updated config key = %s, value = %s version = %s \n",k,v.Value,v.Version)}})iferr!=nil{panic(err)}time.Sleep(20*time.Second)}
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
dapr run --app-id orderprocessing -- go run main.go
import{CommunicationProtocolEnum,DaprClient}from"@dapr/dapr";// JS SDK does not support Configuration API over HTTP protocol yet
constprotocol=CommunicationProtocolEnum.GRPC;consthost=process.env.DAPR_HOST??"localhost";constport=process.env.DAPR_GRPC_PORT??3500;constDAPR_CONFIGURATION_STORE="configstore";constCONFIGURATION_ITEMS=["orderId1","orderId2"];asyncfunctionmain(){constclient=newDaprClient(host,port,protocol);// Subscribe to config updates
try{conststream=awaitclient.configuration.subscribeWithKeys(DAPR_CONFIGURATION_STORE,CONFIGURATION_ITEMS,(config)=>{console.log("Configuration update",JSON.stringify(config.items));});// Unsubscribe to config updates and exit app after 20 seconds
setTimeout(()=>{stream.stop();console.log("App unsubscribed to config changes");process.exit(0);},20000);}catch(error){console.log("Error subscribing to config updates, err:"+error);process.exit(1);}}main().catch((e)=>console.error(e));
Navigate to the directory containing the above code, then run the following command to launch both a Dapr sidecar and the subscriber application:
After you’ve subscribed to watch configuration items, you will receive updates for all of the subscribed keys. To stop receiving updates, you need to explicitly call the unsubscribe API.
Following are the code examples showing how you can unsubscribe to configuration updates using unsubscribe API.
usingSystem;usingSystem.Collections.Generic;usingSystem.Threading.Tasks;usingDapr.Client;varbuilder=WebApplication.CreateBuilder();builder.Services.AddDaprClient();varapp=builder.Build();conststringDAPR_CONFIGURATION_STORE="configstore";conststringSubscriptionId="abc123";//Replace with the subscription identifier to unsubscribe fromvarclient=app.Services.GetRequiredService<DaprClient>();awaitclient.UnsubscribeConfiguration(DAPR_CONFIGURATION_STORE,SubscriptionId);Console.WriteLine("App unsubscribed from config changes");
importio.dapr.client.DaprClientBuilder;importio.dapr.client.DaprClient;importio.dapr.client.domain.ConfigurationItem;importio.dapr.client.domain.GetConfigurationRequest;importio.dapr.client.domain.SubscribeConfigurationRequest;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;//codeprivatestaticfinalStringCONFIG_STORE_NAME="configstore";privatestaticStringsubscriptionId=null;publicstaticvoidmain(String[]args)throwsException{try(DaprClientclient=(newDaprClientBuilder()).build()){// Unsubscribe from config changesUnsubscribeConfigurationResponseunsubscribe=client.unsubscribeConfiguration(subscriptionId,DAPR_CONFIGURATON_STORE).block();if(unsubscribe.getIsUnsubscribed()){System.out.println("App unsubscribed to config changes");}else{System.out.println("Error unsubscribing to config updates, err:"+unsubscribe.getMessage());}}catch(Exceptione){System.out.println("Error unsubscribing to config updates,"+e.getMessage());System.exit(1);}}
import{CommunicationProtocolEnum,DaprClient}from"@dapr/dapr";// JS SDK does not support Configuration API over HTTP protocol yet
constprotocol=CommunicationProtocolEnum.GRPC;consthost=process.env.DAPR_HOST??"localhost";constport=process.env.DAPR_GRPC_PORT??3500;constDAPR_CONFIGURATION_STORE="configstore";constCONFIGURATION_ITEMS=["orderId1","orderId2"];asyncfunctionmain(){constclient=newDaprClient(host,port,protocol);try{conststream=awaitclient.configuration.subscribeWithKeys(DAPR_CONFIGURATION_STORE,CONFIGURATION_ITEMS,(config)=>{console.log("Configuration update",JSON.stringify(config.items));});setTimeout(()=>{// Unsubscribe to config updates
stream.stop();console.log("App unsubscribed to config changes");process.exit(0);},20000);}catch(error){console.log("Error subscribing to config updates, err:"+error);process.exit(1);}}main().catch((e)=>console.error(e));