ClickHouse
forze[clickhouse] implements the analytics contracts on ClickHouse — named,
parameterized SQL against pre-provisioned tables, plus optional append. Like
BigQuery, this is warehouse reads, not document storage.
Install¶
uv add 'forze[clickhouse]'
Needs a ClickHouse server.
The client¶
from forze_clickhouse import ClickHouseClient
ch = ClickHouseClient()
RoutedClickHouseClient resolves per-tenant host/database/credentials.
Settings¶
ClickHouseSettings is the mountable form of ClickHouseConfig: the same host, port,
credentials and database, as a pydantic model your settings root can carry. With secure
set and no port given it resolves 8443 rather than ClickHouseConfig's 8123, which is the
plaintext listener. See connection settings.
Wire it¶
Each analytics route maps query_keys to SQL, keyed by AnalyticsSpec.name:
from forze.application.execution import DepsRegistry, LifecyclePlan
from forze_clickhouse import (
ClickHouseAnalyticsConfig,
ClickHouseClient,
ClickHouseConfig,
ClickHouseDepsModule,
ClickHouseQueryConfig,
clickhouse_lifecycle_step,
)
events = ClickHouseAnalyticsConfig(
database="analytics",
queries={"daily": ClickHouseQueryConfig(sql="SELECT day, value FROM analytics.metrics WHERE day = {day:Date}")},
)
deps = DepsRegistry.from_modules(ClickHouseDepsModule(client=ch, analytics={"events": events}))
lifecycle = LifecyclePlan.from_steps(
clickhouse_lifecycle_step(connection=ClickHouseConfig(host="localhost", port=8123)),
)
What it provides¶
| Contract | Keyed by |
|---|---|
Analytics query (run / run_page / run_cursor / run_chunked) |
AnalyticsSpec.name |
Analytics ingest (append) |
AnalyticsSpec.name (ingest_relation) |
Notes¶
- Tables are pre-provisioned; queries are named SQL with server-side
{field:Type}params bound from the params model. Read SQL isn't rewritten. - The lifecycle step takes
connection=ClickHouseConfig(...)(host, port, credentials, database). - For stable deep pagination, set
cursor_columnon the query config (keyset) instead of offset cursors.