Configuration
Configuration File
Section titled “Configuration File”Create application.yaml in your project root:
app_name: my-appdebug: falseenv: development # development | staging | production | test
logging: level: INFO format: json # text | json
web: host: "0.0.0.0" port: 8000 cors: enabled: true allow_origins: ["https://myapp.com"]
db: url: "postgresql://localhost:5432/myapp" pool_size: 10
cache: backend: memory ttl: 300Loading Config
Section titled “Loading Config”from lexigram import LexigramConfig
# Auto-discovers application.yaml in the project rootconfig = LexigramConfig.from_yaml()
# Or from a specific pathconfig = LexigramConfig.from_yaml("path/to/application.yaml")LexigramConfig is a dataclass with typed top-level fields:
| Field | Type | Default | Description |
|---|---|---|---|
app_name | str | "lexigram-app" | Application name |
debug | bool | False | Debug mode (also via LEX_DEBUG) |
env | Environment | development | Deployment environment |
logging | LoggingConfig | {} | Structured logging settings |
modules | list[str] | [] | Enabled modules |
All additional sections (e.g. web:, db:, cache:) are stored in model_extra and accessed via config.get_section().
Environment Variables
Section titled “Environment Variables”Use ${VAR_NAME} syntax for secrets and deployment-specific values. Provide defaults with ${VAR_NAME:default}:
db: url: "${DATABASE_URL:sqlite:///./dev.db}" password: "${DB_PASSWORD}"
web: port: "${PORT:8000}"Standard Environment Variables
Section titled “Standard Environment Variables”| Variable | Purpose | Default |
|---|---|---|
LEX_PROFILE | Active configuration profile | (none) |
LEX_DEBUG | Enable debug mode | false |
LEX_QUIET | Suppress startup banner | false |
Profile Overlays
Section titled “Profile Overlays”Lexigram merges profile-specific YAML over the base config. Set LEX_PROFILE to activate:
application.yaml # Base config (always loaded)application.development.yaml # Merged when profile=developmentapplication.staging.yaml # Merged when profile=stagingapplication.production.yaml # Merged when profile=productionlexigram.test.yaml # Merged when profile=testExample Profiles
Section titled “Example Profiles”debug: truelogging: level: DEBUG format: textdb: url: "sqlite:///./dev.db"debug: falselogging: level: WARNING format: jsondb: url: "${DATABASE_URL}"cache: backend: redisLoading With Profile
Section titled “Loading With Profile”from lexigram import LexigramConfig
# From environment variable (reads LEX_PROFILE)config = LexigramConfig.from_env_profile()
# Explicit profileconfig = LexigramConfig.from_env_profile("production")
# With custom base pathconfig = LexigramConfig.from_env_profile("staging", base_path="./config")Environment Validation
Section titled “Environment Validation”LexigramConfig.validate_for_environment() checks environment-specific constraints:
issues = config.validate_for_environment(Environment.PRODUCTION)# Returns ConfigIssue if debug=True in productionProvider Config Auto-Injection
Section titled “Provider Config Auto-Injection”Providers declare config_key and config_model to automatically receive their typed config section — no manual parsing required.
from dataclasses import dataclassfrom lexigram.di.provider import Providerfrom lexigram.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: # self.config is a typed BillingConfig instance cfg = self.config or BillingConfig() container.singleton(StripeClient, StripeClient(cfg.stripe_key))How It Works
Section titled “How It Works”During Application.start(), the ProviderOrchestrator:
- Checks if the provider declares
config_keyandconfig_model - Calls
LexigramConfig.get_section(config_key, config_model) - Assigns the result to
provider.config - Then calls
provider.register(container)
Every built-in provider uses this pattern:
| Provider | config_key | config_model |
|---|---|---|
WebProvider | "web" | WebConfig |
DatabaseProvider | "db" | DatabaseConfig |
CacheProvider | "cache" | CacheConfig |
AuthProvider | "auth" | AuthConfig |
Config API
Section titled “Config API”config = LexigramConfig.from_yaml()
# Typed access to known sectionsconfig.app_name # "my-app"config.debug # Falseconfig.env # Environment.DEVELOPMENTconfig.environment # alias for config.env
# Dynamic section access (for plugin/extension sections)db_config = config.get_section("db", DatabaseConfig)raw_dict = config.get_section("payments")
# Dotted path traversalrag_config = config.get_section("ai.rag", RagConfig)
# Check existenceconfig.has_section("web") # True
# Serialize (secrets redacted by default)config.to_dict() # {"app_name": "...", "db": {"password": "***"}}config.to_dict(redact_secrets=False) # full valuesNext Steps
Section titled “Next Steps”- Core Concepts — Providers, DI, and the Result type
- Your First App — Build a working API