Skip to content

Config Platform - SDK Usage Guide

Overview

This guide provides concrete examples and patterns for using Config Platform SDKs in various languages and frameworks. Use these patterns to generate SDK-consuming code in applications.

.NET SDK

Installation

dotnet add package ConnectSoft.ConfigPlatform.Sdk

Basic Configuration

using ConnectSoft.ConfigPlatform.Sdk;

// In Program.cs or Startup.cs
services.AddConfigPlatform(options =>
{
    options.BaseUrl = "https://config.connectsoft.cloud";
    options.TenantId = configuration["ConfigPlatform:TenantId"];
    options.ClientId = configuration["ConfigPlatform:ClientId"];
    options.ClientSecret = configuration["ConfigPlatform:ClientSecret"];
    options.Environment = "production";
});

Resolving Configuration

Direct Resolution:

public class PaymentService
{
    private readonly IConfigClient _configClient;

    public PaymentService(IConfigClient configClient)
    {
        _configClient = configClient;
    }

    public async Task<string> GetApiUrlAsync()
    {
        var config = await _configClient.ResolveAsync("payment.apiUrl");
        return config.Value.ToString();
    }
}

Options Pattern Integration:

// Define options class
public class PaymentServiceOptions
{
    public string ApiUrl { get; set; }
    public int RetryCount { get; set; }
    public TimeSpan Timeout { get; set; }
}

// Register with Config Platform binding
services.AddConfigPlatformBinding<PaymentServiceOptions>(
    configSetId: "payment-service",
    path: "payment"
);

// Use in service
public class PaymentService
{
    private readonly IOptionsSnapshot<PaymentServiceOptions> _options;

    public PaymentService(IOptionsSnapshot<PaymentServiceOptions> options)
    {
        _options = options;
    }

    public async Task ProcessPaymentAsync()
    {
        var apiUrl = _options.Value.ApiUrl;
        var retryCount = _options.Value.RetryCount;
        // Use options...
    }
}

Hot Reload Pattern

services.AddConfigPlatform(options =>
{
    options.EnableHotReload = true;
    options.RefreshInterval = TimeSpan.FromMinutes(5);
    options.RefreshChannel = RefreshChannel.WebSocket;
});

// Subscribe to changes
public class ConfigChangeHandler : IConfigChangeHandler
{
    public Task OnConfigChangedAsync(ConfigChangeEvent changeEvent)
    {
        // Reload options, update cache, restart services, etc.
        Console.WriteLine($"Config changed: {changeEvent.Path} = {changeEvent.Value}");
        return Task.CompletedTask;
    }
}

// Register handler
services.AddConfigChangeHandler<ConfigChangeHandler>();

Caching and ETag Support

public class CachedConfigService
{
    private readonly IConfigClient _configClient;
    private readonly IMemoryCache _cache;

    public async Task<T> GetConfigAsync<T>(string path)
    {
        var cacheKey = $"config:{path}";

        if (_cache.TryGetValue(cacheKey, out CachedConfig<T> cached))
        {
            // Check if still valid using ETag
            var current = await _configClient.ResolveAsync(path, cached.ETag);
            if (current.ETag == cached.ETag)
            {
                return cached.Value; // Not modified
            }

            // Update cache
            _cache.Set(cacheKey, new CachedConfig<T>
            {
                Value = current.Value,
                ETag = current.ETag
            }, TimeSpan.FromHours(1));

            return current.Value;
        }

        // First load
        var config = await _configClient.ResolveAsync(path);
        _cache.Set(cacheKey, new CachedConfig<T>
        {
            Value = config.Value,
            ETag = config.ETag
        }, TimeSpan.FromHours(1));

        return config.Value;
    }
}

Error Handling

public class ResilientConfigService
{
    private readonly IConfigClient _configClient;
    private readonly ILogger<ResilientConfigService> _logger;

    public async Task<string> GetConfigWithFallbackAsync(string path, string defaultValue)
    {
        try
        {
            var config = await _configClient.ResolveAsync(path);
            return config.Value.ToString();
        }
        catch (ConfigNotFoundException ex)
        {
            _logger.LogWarning(ex, "Config not found: {Path}, using default", path);
            return defaultValue;
        }
        catch (ConfigPlatformException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
        {
            _logger.LogError(ex, "Config Platform unavailable, using cached/default value");
            return defaultValue;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unexpected error loading config: {Path}", path);
            throw;
        }
    }
}

Batch Resolution

public async Task<Dictionary<string, object>> GetMultipleConfigsAsync(
    IEnumerable<string> paths)
{
    var requests = paths.Select(path => 
        _configClient.ResolveAsync(path)
    );

    var results = await Task.WhenAll(requests);

    return results.ToDictionary(
        r => r.Path,
        r => r.Value
    );
}

JavaScript/TypeScript SDK

Installation

npm install @connectsoft/config-platform-sdk

Basic Usage

import { ConfigClient } from '@connectsoft/config-platform-sdk';

const client = new ConfigClient({
  baseUrl: 'https://config.connectsoft.cloud',
  tenantId: process.env.CONFIG_TENANT_ID,
  clientId: process.env.CONFIG_CLIENT_ID,
  clientSecret: process.env.CONFIG_CLIENT_SECRET,
  environment: 'production'
});

// Resolve configuration
const config = await client.resolve('api.baseUrl');
console.log(config.value);

React Hook Pattern

import { useConfig } from '@connectsoft/config-platform-sdk/react';

function PaymentComponent() {
  const { config, loading, error, refresh } = useConfig('payment.apiUrl', {
    environment: 'production',
    enableHotReload: true
  });

  if (loading) return <div>Loading configuration...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <p>API URL: {config.value}</p>
      <button onClick={refresh}>Refresh</button>
    </div>
  );
}

Next.js Integration

// lib/config.ts
import { ConfigClient } from '@connectsoft/config-platform-sdk';

let configClient: ConfigClient | null = null;

export function getConfigClient(): ConfigClient {
  if (!configClient) {
    configClient = new ConfigClient({
      baseUrl: process.env.CONFIG_BASE_URL!,
      tenantId: process.env.CONFIG_TENANT_ID!,
      clientId: process.env.CONFIG_CLIENT_ID!,
      clientSecret: process.env.CONFIG_CLIENT_SECRET!
    });
  }
  return configClient;
}

// app/api/payment/route.ts
import { getConfigClient } from '@/lib/config';

export async function GET() {
  const client = getConfigClient();
  const config = await client.resolve('payment.apiUrl');

  return Response.json({ apiUrl: config.value });
}

Hot Reload with WebSocket

const client = new ConfigClient({
  enableHotReload: true,
  refreshChannel: 'websocket'
});

client.onConfigChanged((event) => {
  console.log('Config changed:', event.path, event.value);

  // Update application state
  if (event.path === 'payment.apiUrl') {
    updateApiUrl(event.value);
  }
});

Python SDK

Installation

pip install connectsoft-config-platform-sdk

Basic Usage

from connectsoft_config_platform import ConfigClient

client = ConfigClient(
    base_url="https://config.connectsoft.cloud",
    tenant_id=os.getenv("CONFIG_TENANT_ID"),
    client_id=os.getenv("CONFIG_CLIENT_ID"),
    client_secret=os.getenv("CONFIG_CLIENT_SECRET"),
    environment="production"
)

# Resolve configuration
config = client.resolve("api.baseUrl")
print(config.value)

Django Integration

# settings.py
INSTALLED_APPS = [
    'connectsoft_config_platform.django',
    # ...
]

CONFIG_PLATFORM = {
    'BASE_URL': os.getenv('CONFIG_BASE_URL'),
    'TENANT_ID': os.getenv('CONFIG_TENANT_ID'),
    'CLIENT_ID': os.getenv('CONFIG_CLIENT_ID'),
    'CLIENT_SECRET': os.getenv('CONFIG_CLIENT_SECRET'),
}

# views.py
from connectsoft_config_platform.django import get_config

def payment_view(request):
    api_url = get_config('payment.apiUrl')
    # Use api_url...

Best Practices

1. Use Options Pattern

Prefer binding configuration to strongly-typed options classes rather than direct resolution:

// Good
services.AddConfigPlatformBinding<PaymentServiceOptions>("payment-service", "payment");

// Avoid
var apiUrl = await _configClient.ResolveAsync("payment.apiUrl");

2. Enable Caching

Always enable caching for production workloads:

services.AddConfigPlatform(options =>
{
    options.EnableCaching = true;
    options.CacheTTL = TimeSpan.FromHours(1);
});

3. Handle Errors Gracefully

Always provide fallback values for critical configuration:

var apiUrl = await GetConfigWithFallbackAsync(
    "payment.apiUrl",
    "https://api.payment.default.com"
);

4. Use ETags for Efficiency

Leverage ETags to minimize network traffic:

var config = await _configClient.ResolveAsync(path, etag: cachedETag);
if (config.ETag == cachedETag)
{
    // Use cached value
}

5. Monitor Configuration Changes

Subscribe to change events for dynamic configuration:

services.AddConfigChangeHandler<ConfigChangeHandler>();

6. Validate Configuration on Startup

Validate required configuration exists at application startup:

public class ConfigValidator
{
    private readonly IConfigClient _configClient;

    public async Task ValidateAsync()
    {
        var requiredPaths = new[] { "payment.apiUrl", "payment.apiKey" };

        foreach (var path in requiredPaths)
        {
            try
            {
                await _configClient.ResolveAsync(path);
            }
            catch (ConfigNotFoundException)
            {
                throw new InvalidOperationException(
                    $"Required configuration missing: {path}"
                );
            }
        }
    }
}

Performance Considerations

Connection Pooling

SDK automatically manages HTTP connection pooling. Configure pool size if needed:

services.AddConfigPlatform(options =>
{
    options.HttpClientOptions = new HttpClientOptions
    {
        MaxConnectionsPerServer = 10,
        PooledConnectionLifetime = TimeSpan.FromMinutes(5)
    };
});

Request Batching

Batch multiple configuration requests when possible:

var batch = await _configClient.ResolveBatchAsync(new[]
{
    "payment.apiUrl",
    "payment.timeout",
    "payment.retryCount"
});

References