- Docs
- Core
- Getting started
- Configuration
Configuration
Configuration File
Section titled “Configuration File”Create application.yaml in your project root. Top-level keys are core settings; each extension reads its own section (the section name is the provider’s config_key):
app_name: my-appdebug: falseenv: development # development | staging | production | test
logging: level: INFO json_format: true # true | false
# quadkit-web (name: "web")web: server: host: "0.0.0.0" port: 8000 cors: enabled: true allow_origins: ["https://myapp.com"]
# quadkit-cli (config_section: "cli")cli: enabled: true color: trueLoading Config
Section titled “Loading Config”from quadkit import QuadKitConfig
# Auto-discovers application.yaml in the project rootconfig = QuadKitConfig.from_yaml()
# Or from a specific pathconfig = QuadKitConfig.from_yaml("path/to/application.yaml")Application loads configuration for you when you don’t pass one — it calls QuadKitConfig.from_env_profile() by default.
QuadKitConfig has typed top-level fields:
| Field | Type | Default | Description |
|---|---|---|---|
app_name | str | "quadkit-app" | Application name |
debug | bool | False | Debug mode |
env | Environment | development | Deployment environment |
logging | LoggingConfig | — | Structured logging settings |
modules | list[str] | [] | Enabled modules |
Extension sections (web:, cli:, …) are accessed via config.get_section().
Environment Variables
Section titled “Environment Variables”There are two complementary mechanisms.
1. Interpolation inside YAML
Section titled “1. Interpolation inside YAML”Use ${VAR} for secrets and deployment values, with optional defaults via ${VAR:default}:
web: server: host: "${HOST:0.0.0.0}" port: "${PORT:8000}"2. Override any key with QK_ env vars
Section titled “2. Override any key with QK_ env vars”Any configuration key can be overridden by an environment variable using the QK_ prefix and double underscores for nesting. Env vars win over YAML:
QK_WEB__SERVER__PORT=9000 # web.server.port = 9000QK_WEB__SECURITY__CORS__ALLOW_ORIGINS__0=https://myapp.com # list items use numeric indicesStandard variables
Section titled “Standard variables”| Variable | Purpose | Default |
|---|---|---|
QK_PROFILE | Active configuration profile | (none) |
QK_DEBUG | Enable debug mode | false |
QK_QUIET | Suppress startup banner | false |
QK_ENV | Deployment environment | development |
Profile Overlays
Section titled “Profile Overlays”QuadKit merges a profile-specific YAML over the base config. Set QK_PROFILE to activate it:
application.yaml # Base config (always loaded)application.development.yaml # Merged when QK_PROFILE=developmentapplication.staging.yaml # Merged when QK_PROFILE=stagingapplication.production.yaml # Merged when QK_PROFILE=productionapplication.test.yaml # Merged when QK_PROFILE=testExample profiles
Section titled “Example profiles”debug: truelogging: level: DEBUG json_format: falseweb: server: port: 9000debug: falselogging: level: WARNING json_format: truecache: backends: - name: redis type: redis default: true redis_url: "${REDIS_URL}"Loading with a profile
Section titled “Loading with a profile”from quadkit import QuadKitConfig
# Reads QK_PROFILE from the environmentconfig = QuadKitConfig.from_env_profile()
# Explicit profileconfig = QuadKitConfig.from_env_profile("production")
# With a custom base pathconfig = QuadKitConfig.from_env_profile("staging", base_path="./config")Environment validation
Section titled “Environment validation”validate_for_environment() checks environment-specific constraints (for example, debug=True in production):
from quadkit.contracts.core.config import Environment
issues = config.validate_for_environment(Environment.PRODUCTION)Provider Config Auto-Injection
Section titled “Provider Config Auto-Injection”A provider declares config_key and config_model to automatically receive its typed config section — no manual parsing:
from dataclasses import dataclassfrom quadkit import Providerfrom quadkit.contracts.core.di import ContainerRegistrarProtocol
@dataclassclass BillingConfig: stripe_key: str = "" currency: str = "usd"
class BillingProvider(Provider): name = "billing" config_key = "billing" # reads "billing:" from application.yaml config_model = BillingConfig # coerces it into BillingConfig
async def register(self, container: ContainerRegistrarProtocol) -> None: cfg = self.config or BillingConfig() # self.config is a typed BillingConfig container.singleton(StripeClient, StripeClient(cfg.stripe_key))Before calling register(), the framework reads the matching section via QuadKitConfig.get_section(config_key, config_model) and assigns it to provider.config. Built-in providers use the same mechanism:
| Provider | config_key |
|---|---|
DatabaseProvider | "sql" |
CacheProvider | "cache" |
AuthProvider | "auth" |
Config API
Section titled “Config API”config = QuadKitConfig.from_yaml()
# Typed top-level accessconfig.app_name # "my-app"config.debug # Falseconfig.environment # Environment.DEVELOPMENT
# Section access (extension config)db_config = config.get_section("sql", DatabaseConfig)rag_config = config.get_section("ai_rag", RAGConfig) # dotted paths also supported
# Existence + serialization (secrets redacted by default)config.has_section("web") # Trueconfig.to_dict() # {"app_name": "...", "auth": {"secret_key": "***"}}config.to_dict(redact_secrets=False) # full valuesNext Steps
Section titled “Next Steps”- YAML Configuration — interpolation, precedence, and profiles in depth
- The quadkit CLI —
config show,config doctor,config env - Core Concepts — Providers, DI, and the Result type
- Your First App — Build a working API
- Project Structure — where
application.yamlsits