ClickHouse

Production-grade OLAP AnalyticsSink over ClickHouse — self-hosted or ClickHouse Cloud — for billions of events with millisecond aggregates.

@voltro/plugin-clickhouse is the production AnalyticsSink for analytics at scale — native HTTP ingest into a ClickHouse cluster (self-hosted or ClickHouse Cloud, both via the official @clickhouse/client). The sink owns an events table inside the configured database (a MergeTree engine created on first boot) and implements all four contract methods. Reach for it when postgres-lite hits its ~10M events/day ceiling and you need millisecond aggregate queries over billions of rows. For the shared AnalyticsSink API and the useAnalytics() read methods, see Analytics & warehouse sinks.

Install

pnpm add @voltro/plugin-clickhouse

Wiring

// app.config.ts
import { clickhouseAnalytics } from '@voltro/plugin-clickhouse'

export default {
  type:      'api' as const,
  name:      'myApi',
  store:     'postgres' as const,
  analytics: clickhouseAnalytics({
    url:      process.env.CLICKHOUSE_URL!,   // https://my-cluster.clickhouse.cloud
    username: process.env.CLICKHOUSE_USER,
    password: process.env.CLICKHOUSE_PASSWORD,
    database: 'voltro_events',               // optional, default 'default'
  }),
}

At boot the plugin opens the client, pings the cluster (fail-fast on bad config), and creates the events table + any mirror tables. The client is closed on graceful shutdown.

Options

clickhouseAnalytics(options):

Option Type Default Notes
url string — (required) HTTP URL of the ClickHouse server, incl. protocol + port.
username string
password string
database string 'default' The plugin creates the events table inside it on first boot.
table string 'events' Override the events-table name.
mirrorTables ReadonlyArray<string> [] (events-only) Reactive tables to CDC-mirror into voltro_mirror_<table> (a ReplacingMergeTree) so analytical queries JOIN events against live user data.
mirrorPrimaryKey string 'id' Primary-key column on the mirrored source rows.

database, table, and each mirrorTables entry are validated as SQL identifiers at boot (they're interpolated into DDL) — a bad name fails loudly at app.config eval, not on the first query.

Connection details come from options, not env. The plugin reads no CLICKHOUSE_* variables itself — the example above wires process.env into the options. Use @voltro/env's defineEnv to declare them as secrets.

No raw-client escape hatch

ClickHouse-specific features outside the cross-provider contract (HyperLogLog, dictionaries, materialised views) are not reachable through the plugin — it exposes no raw @clickhouse/client handle; the AnalyticsSink contract is the extension seam. Where you need them, query ClickHouse with your own client instance against the same tables.

Using the Tag binds your handler to ClickHouse — stay on useAnalytics() for provider-portable code. The plugin also exports EVENTS_TABLE_DDL and MIRROR_TABLE_DDL for advanced schema work.

See also

  • Analytics & warehouse sinks — the shared AnalyticsSink contract, the useAnalytics() read API, composeAnalytics, and the CDC-mirror details.