Multi-Environment Configuration¶
This guide covers practical steps for setting up and managing feature flags across multiple deployment environments.
See also
For detailed API reference and conceptual overview, see Multi-Environment Support.
Setting Up Environment Hierarchy¶
Create a standard dev/staging/production pipeline:
from litestar_flags.models.environment import Environment
async def setup_environments(storage) -> None:
"""Create standard environment hierarchy."""
# Root: Production (no parent)
production = await storage.create_environment(
Environment(
name="Production",
slug="production",
description="Live production environment",
settings={"require_approval": True},
)
)
# Staging inherits from production
staging = await storage.create_environment(
Environment(
name="Staging",
slug="staging",
parent_id=production.id,
settings={"require_approval": False},
)
)
# Development inherits from staging
await storage.create_environment(
Environment(
name="Development",
slug="development",
parent_id=staging.id,
settings={"debug_mode": True},
)
)
Configuring Environment Detection¶
Enable automatic environment detection from HTTP requests:
from litestar import Litestar
from litestar_flags import FeatureFlagsConfig, FeatureFlagsPlugin
config = FeatureFlagsConfig(
backend="database",
connection_string="postgresql+asyncpg://user:pass@localhost/db",
# Environment detection
enable_environment_middleware=True,
environment_header="X-Environment",
environment_query_param="env",
default_environment="production",
allowed_environments=["production", "staging", "development"],
)
app = Litestar(
route_handlers=[...],
plugins=[FeatureFlagsPlugin(config=config)],
)
Request examples:
# Via header
curl -H "X-Environment: staging" https://api.example.com/feature
# Via query param
curl "https://api.example.com/feature?env=development"
# Falls back to default
curl https://api.example.com/feature
Creating Environment-Specific Overrides¶
Override flag values for specific environments:
from litestar_flags.models.environment_flag import EnvironmentFlag
async def configure_flag_per_environment(storage, flag_key: str) -> None:
"""Set different rollout percentages per environment."""
flag = await storage.get_flag(flag_key)
staging = await storage.get_environment("staging")
production = await storage.get_environment("production")
# 100% in staging for full testing
await storage.create_environment_flag(
EnvironmentFlag(
environment_id=staging.id,
flag_id=flag.id,
enabled=True,
percentage=100.0,
)
)
# 10% gradual rollout in production
await storage.create_environment_flag(
EnvironmentFlag(
environment_id=production.id,
flag_id=flag.id,
enabled=True,
percentage=10.0,
)
)
# Development inherits staging's 100% (no override needed)
Evaluating Flags with Environment Context¶
Use the resolver to get environment-specific flag values:
from litestar_flags.environment import EnvironmentResolver
async def check_flag_per_environment(storage, flag_key: str) -> None:
"""Demonstrate environment-aware flag evaluation."""
resolver = EnvironmentResolver(storage)
base_flag = await storage.get_flag(flag_key)
# Each environment gets its resolved configuration
for env_slug in ["production", "staging", "development"]:
resolved = await resolver.resolve_flag_for_environment(
flag=base_flag,
environment_slug=env_slug,
)
print(f"{env_slug}: enabled={resolved.default_enabled}")
Promoting Flags Between Environments¶
Copy flag configuration from one environment to another:
from litestar_flags.models.environment_flag import EnvironmentFlag
async def promote_flag(
storage,
flag_key: str,
from_env: str,
to_env: str,
) -> None:
"""Promote flag configuration between environments."""
source = await storage.get_environment(from_env)
target = await storage.get_environment(to_env)
flag = await storage.get_flag(flag_key)
source_override = await storage.get_environment_flag(
env_id=source.id,
flag_id=flag.id,
)
if not source_override:
raise ValueError(f"No override for {flag_key} in {from_env}")
existing = await storage.get_environment_flag(
env_id=target.id,
flag_id=flag.id,
)
if existing:
existing.enabled = source_override.enabled
existing.percentage = source_override.percentage
existing.rules = source_override.rules
await storage.update_environment_flag(existing)
else:
await storage.create_environment_flag(
EnvironmentFlag(
environment_id=target.id,
flag_id=flag.id,
enabled=source_override.enabled,
percentage=source_override.percentage,
rules=source_override.rules,
)
)
# Usage: promote from staging to production
await promote_flag(storage, "new-checkout", "staging", "production")
Accessing Environment in Route Handlers¶
Get the detected environment within your handlers:
from litestar import get
from litestar.connection import Request
from litestar_flags import get_request_environment
@get("/debug/environment")
async def debug_environment(request: Request) -> dict:
"""Return the current request environment."""
environment = get_request_environment(request.scope)
return {"environment": environment}
Inspecting the Inheritance Chain¶
Debug which environment provides a flag’s value:
from litestar_flags.environment import EnvironmentResolver
async def debug_inheritance(storage, env_slug: str) -> None:
"""Print the inheritance chain for an environment."""
resolver = EnvironmentResolver(storage)
chain = await resolver.get_environment_chain(env_slug)
print(f"Inheritance chain for '{env_slug}':")
for env in chain:
print(f" -> {env.slug} ({env.name})")
# Example output for "development":
# -> development (Development)
# -> staging (Staging)
# -> production (Production)
Production Security Configuration¶
Lock down environment detection in production:
import os
from litestar_flags import FeatureFlagsConfig
config = FeatureFlagsConfig(
backend="database",
connection_string=os.environ["DATABASE_URL"],
# Strict production settings
default_environment="production",
enable_environment_middleware=True,
environment_header="X-Environment",
environment_query_param=None, # Disable query param
allowed_environments=["production"], # Only allow production
)
Gradual Rollout Workflow¶
Standard pattern for releasing a feature across environments:
# Step 1: Enable 100% in development
await storage.create_environment_flag(
EnvironmentFlag(environment_id=dev.id, flag_id=flag.id, percentage=100)
)
# Test thoroughly...
# Step 2: Enable 100% in staging
await storage.create_environment_flag(
EnvironmentFlag(environment_id=staging.id, flag_id=flag.id, percentage=100)
)
# Validate with production-like data...
# Step 3: Gradual production rollout
prod_flag = EnvironmentFlag(
environment_id=production.id, flag_id=flag.id, enabled=True, percentage=5
)
await storage.create_environment_flag(prod_flag)
# Monitor metrics, then increase...
prod_flag.percentage = 25
await storage.update_environment_flag(prod_flag)
# Monitor, then continue to 100%...