Skip to content

Config Management

Most customers manage configs through the Console UI. The management API is for infrastructure-as-code, CI/CD pipelines, setup scripts, and automated testing.

Management runs through the config surface of the smplkit client (SmplClient, or AsyncSmplClient for async) — client.config (Python, TypeScript, Java), client.Config (C#), or client.Config() (Go).

Create a Config

Create a new config with the factory method, set items via the typed setters, then call save() to persist. All mutations are local until save() is called.

python
import asyncio
from smplkit import AsyncSmplClient

async with AsyncSmplClient() as client:
    user_service = client.config.new(
        "user-service",
        name="User Service",
        description="Configuration for the user microservice.",
    )
    user_service.set_string("database.host", "localhost")
    user_service.set_number("database.port", 5432)
    user_service.set_number("database.pool_size", 5)
    user_service.set_number("cache_ttl_seconds", 300)
    user_service.set_boolean("enable_signup", True)
    await user_service.save()

Item Types

Every config item has one of four types, set via the matching typed setter:

TypePython / TS / Java / C# / GoNotes
STRINGset_string / setString / SetStringPlain strings
NUMBERset_number / setNumber / SetNumberIntegers and floats
BOOLEANset_boolean / setBoolean / SetBooleantrue / false
JSONset_json / setJson / SetJsonArbitrary JSON-serializable values

The type is locked in once an item is created — calling set_string("max_retries", "5") and later set_number("max_retries", 5) is an error. Pick the right type at creation time.

Environment Overrides

Each item can have per-environment overrides. The runtime resolves the active environment's override; if none is set, the base value is used.

python
user_service.set_string(
    "database.host",
    "prod-users-rds.internal.acme.dev",
    environment="production",
)
user_service.set_number("database.pool_size", 20, environment="production")
user_service.set_number("cache_ttl_seconds", 600, environment="production")
user_service.set_boolean("enable_signup", False, environment="production")
await user_service.save()

Parent–Child Configs

A config can have a parent. Resolved values from the parent are inherited; values defined in the child override the parent. The hierarchy is at most two levels deep — the server rejects a parent that itself has a parent.

python
shared = client.config.new(
    "common",
    name="Common",
    description="Shared defaults inherited by all services.",
)
shared.set_string("app_name", "Acme SaaS Platform")
shared.set_number("max_retries", 3)
await shared.save()

user_service = client.config.new(
    "user-service",
    name="User Service",
    parent=shared,  # or parent="common" — pass either an id or a Config
)
user_service.set_string("database.host", "localhost")
await user_service.save()

Inheritance is opt-in — a config with no parent stands alone. The built-in common config is convenient as a parent for shared defaults, but it is not applied automatically.

List and Get Configs

python
configs = await client.config.list()
for cfg in configs:
    parent_info = f" (parent: {cfg.parent})" if cfg.parent else " (root)"
    print(f"  {cfg.id}{parent_info}")

fetched = await client.config.get("user-service")
print(f"id={fetched.id}, name={fetched.name}, items={list(fetched.items.keys())}")

Update a Config

Fetch a config, mutate via the typed setters, then save. Mutations accumulate locally; a single save() persists everything. Re-call set_string("key", value) (without environment=) to overwrite the base value, or with environment=... to overwrite the override.

python
fetched = await client.config.get("user-service")
fetched.description = "User microservice — updated description"
fetched.set_number("cache_ttl_seconds", 900, environment="production")
await fetched.save()

Delete a Config

Children must be deleted before their parents, or the server will reject the parent delete with a ConflictError.

python
await client.config.delete("user-service")
# or:  await fetched.delete()

Sync Client (Python)

For synchronous applications (Django, Flask, CLI tools), use SmplClient instead of AsyncSmplClient. The API is identical but without await.

python
from smplkit import SmplClient

with SmplClient() as client:
    cfg = client.config.new("my-service", name="My Service")
    cfg.set_string("database.host", "localhost")
    cfg.set_number("max_retries", 3)
    cfg.save()

    fetched = client.config.get("my-service")
    fetched.set_number("max_retries", 5, environment="production")
    fetched.save()

    configs = client.config.list()
    client.config.delete("my-service")

Next Steps