# Clickhouse Buffered analytical event export - Package: @bijectionhq/clickhouse - Category: Analytics - Status: Preview - License: MIT - Bijection source (repository access required): https://github.com/bijectionhq/clickhouse/tree/24cf4910fe82aed34d05e7154fe9d3075a9650e4 - Upstream: https://github.com/robertalv/clickhouse/tree/df2e88357ca0e0f4529229e56d02db7f1a16843e - Documentation: https://docs.bijection.com/components/using - Directory: https://bijection.com/components/clickhouse ## Get the source Bijection repository access is required. Request access at https://bijection.com/contact. ```sh git clone https://github.com/bijectionhq/clickhouse.git cd clickhouse git checkout 24cf4910fe82aed34d05e7154fe9d3075a9650e4 ``` ## Before using this component Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute. ## Upstream package documentation Recorded 2026-10-23. This describes @devwithbobby/clickhouse; check the Bijection adaptation separately. This component buffers analytics events in Convex's database and flushes them in batches to ClickHouse Cloud via the ClickHouse HTTP API. It decouples high-frequency write operations from synchronous external HTTP calls by staging rows locally and draining them through a separate action, typically triggered by a cron or scheduler. Configurable batch size, retry logic, and a read-only query interface round out the integration surface. ### Use cases - Tracking user or workspace events from mutations without blocking on an external HTTP call to ClickHouse - Running scheduled batch exports of buffered analytics rows to ClickHouse Cloud on a cron interval - Querying ClickHouse for read-only analytics results (SELECT, WITH, SHOW, DESCRIBE) from within Convex actions - Building a product analytics pipeline where Convex handles ingestion and ClickHouse handles aggregation and querying - Testing ClickHouse integration logic locally using the provided convex-test helper without a live ClickHouse instance ### How it works Mount the component in `convex/convex.config.ts` using `app.use(clickhouse)`, then instantiate a `ClickHouse` client in a local wrapper file. The constructor accepts your ClickHouse Cloud credentials (url, user, password) pulled from environment variables, along with options for `batchSize` and `maxRetries`. The `insert(ctx, tableName, rows)` method is called from mutations to buffer rows into Convex's database. Table names are validated and quoted before use, accepting formats like `events` or `analytics.events`. No external HTTP call happens at insert time, keeping mutations fast and transactionally safe. The `flush(ctx)` method is called from an action to drain buffered rows to ClickHouse over HTTP. It returns a result object with `flushed`, `failed`, and `errors` fields. This action is typically scheduled via a Convex cron job rather than called inline from user-facing requests. A `query(ctx, sql)` method is also available from actions for running read-only ClickHouse queries. It enforces a whitelist of allowed statement prefixes (SELECT, WITH, SHOW, DESCRIBE, DESC, EXPLAIN). A test helper exported from `@devwithbobby/clickhouse/test` integrates with `convex-test` for local testing without a live ClickHouse connection. ### Upstream README # @devwithbobby/clickhouse Convex component for buffering analytics events in Convex and flushing them to ClickHouse Cloud over the ClickHouse HTTP API. ## Demo Live demo: https://robertalv.github.io/clickhouse/ The demo is a static example site. It does not collect credentials or connect to a shared ClickHouse account. Users bring their own Convex deployment and ClickHouse HTTP credentials. Run it locally: ```sh npm install npm run demo:dev ``` ## Install ```sh npm install @devwithbobby/clickhouse ``` Mount the component in `convex/convex.config.ts`: ```ts import { defineApp } from "convex/server"; import clickhouse from "@devwithbobby/clickhouse/convex.config.js"; const app = defineApp(); app.use(clickhouse); export default app; ``` ## Use Create a small app-local wrapper so credentials stay in your application environment: ```ts // convex/clickhouse.ts import { ClickHouse } from "@devwithbobby/clickhouse"; import { components } from "./_generated/api"; export const clickhouse = new ClickHouse(components.clickhouse, { credentials: () => ({ url: process.env.CLICKHOUSE_URL!, user: process.env.CLICKHOUSE_USER!, password: process.env.CLICKHOUSE_PASSWORD!, }), batchSize: 500, maxRetries: 5, }); ``` Buffer rows from a mutation: ```ts import { v } from "convex/values"; import { mutation } from "./_generated/server"; import { clickhouse } from "./clickhouse"; export const trackEvent = mutation({ args: { workspaceId: v.string(), event: v.string(), }, returns: v.null(), handler: async (ctx, args) => { await clickhouse.insert(ctx, "events", [ { workspace_id: args.workspaceId, event: args.event, created_at: Date.now(), }, ]); return null; }, }); ``` Flush from an action, usually called by a cron or scheduler: ```ts import { v } from "convex/values"; import { action } from "./_generated/server"; import { clickhouse } from "./clickhouse"; export const flushClickHouse = action({ args: {}, returns: v.object({ flushed: v.number(), failed: v.number(), errors: v.array(v.string()), }), handler: async (ctx) => { return await clickhouse.flush(ctx); }, }); ``` Run read-only analytics queries from actions: ```ts const rows = await clickhouse.query<{ count: number }>( ctx, "SELECT count() AS count FROM events", ); ``` ## Table Names and Queries Table names are validated and quoted before ClickHouse inserts. Valid table names look like `events` or `analytics.events`. `query()` only accepts read-style statements beginning with `SELECT`, `WITH`, `SHOW`, `DESCRIBE`, `DESC`, or `EXPLAIN`. ## Component Testing This package exports a test helper for `convex-test`: ```ts import { convexTest } from "convex-test"; import clickhouseTest from "@devwithbobby/clickhouse/test"; const t = convexTest(); clickhouseTest.register(t); ``` ## Publishing Checklist Before publishing: ```sh npm install npm run codegen npm run check-types npm run build npm test npm pack --dry-run ``` Then publish: ```sh npm publish --access public ```