Article

ClickHouse Embedded Analytics for SaaS: 2026 Engineering Guide

ClickHouse embedded analytics for SaaS: build tenant-safe customer dashboards with the QueryPanel Node SDK, realistic load tests, and safer query controls.

QueryPanel Team
13 min read
embedded analyticsClickHouseSaaScustomer-facing analyticsmulti-tenantNode SDKreal-time analytics

ClickHouse can make large event datasets feel interactive. It does not, by itself, decide which tenant a customer may query, constrain an expensive request, or provide the dashboard experience inside your SaaS product.

Last updated July 2026: implementation guidance for engineering teams building customer-facing analytics on ClickHouse, including tenant controls, Node SDK integration, query budgets, and a production-like proof of concept.


Short answer: ClickHouse embedded analytics is a strong fit when a SaaS product needs fast customer-facing exploration over high-volume events, usage records, logs, telemetry, or time-series data. Treat ClickHouse as the analytical serving layer, then put tenant identity, table access, query validation, and product UX around it. QueryPanel's headless Node SDK fits this architecture when your backend should own the ClickHouse connection and your team wants full control over the customer interface.

Do not choose the architecture from a single fast query. ClickHouse's own customer-facing concurrency guidance says sustainable capacity depends on the query mix, data layout, cache state, ingestion load, latency target, hardware, and topology. Your acceptance test should therefore model dashboard fan-out and multiple tenants under live ingestion—not a warm-cache benchmark in a quiet cluster.

Key takeaways

  • ClickHouse is the data layer, not the complete embedded product. Your application still owns authentication, tenant context, saved views, exports, and failure behavior.
  • Headless is the cleanest starting point for engineering-led teams. The QueryPanel Node SDK generates and validates ClickHouse SQL while your backend controls execution and presentation.
  • Tenant safety needs multiple checks. Use a read-only ClickHouse identity, restrict accessible tables, pass server-verified tenant context, and test the database's row controls where they are part of your design.
  • Concurrency is workload-specific. Translate users into dashboard queries, then measure p50, p95, errors, reads, memory, and ingestion lag.
  • Pre-aggregation is selective, not automatic. Add materialized views for stable, repeated access patterns rather than hiding an unclear metric model behind more infrastructure.
  • A POC must include bad paths. Broad date ranges, uneven tenants, cancelled requests, timeouts, and invalid AI-generated queries are part of the product.

ClickHouse embedded analytics architecture for SaaS

A practical implementation has five boundaries:

BoundaryResponsibilityFailure to prevent
Product backendVerify the user and resolve tenantId server-sideA browser choosing another customer's identifier
QueryPanel Node SDKGround the question in known schema, generate dialect-aware SQL, validate, and return chart-ready resultsGeneric SQL that ignores the ClickHouse dialect or available schema
ClickHouse callbackExecute through your controlled client and settingsDatabase credentials or unrestricted execution moving into the browser
ClickHouse access layerApply read-only permissions, row policies where used, and resource controlsCross-tenant reads or a query monopolizing the cluster
Product UIRender results, edits, errors, and saved stateAn analytics experience that feels like a separate BI portal

The request flow should be:

  1. Your backend authenticates the product user.
  2. The backend derives the tenant ID from trusted session or authorization state.
  3. It calls qp.ask() with that tenant ID and the attached ClickHouse database name.
  4. The SDK sends the generated query through the ClickHouse client callback you supplied.
  5. Your API returns only the result and presentation data the product route needs.

This keeps the trust boundary explicit. A dashboard filter can narrow what a user sees, but it should never be the only tenant control.

Connect QueryPanel's Node SDK to ClickHouse

The current SDK method is attachClickhouse. The example below uses the official ClickHouse JavaScript client, restricts schema access to two tables, and declares the tenant field used by the SaaS data model.

import { createClient, type QueryParams } from "@clickhouse/client";
import {
  type ClickHouseClientFn,
  QueryPanelSdkAPI,
} from "@querypanel/node-sdk";

// App-owned wrapper around Supabase Vault's get_secret RPC.
const secrets = await getAnalyticsSecrets();

const clickhouse = createClient({
  url: secrets.clickhouseUrl,
  username: secrets.clickhouseUser,
  password: secrets.clickhousePassword,
  database: secrets.clickhouseDatabase,
});

const executeClickHouse: ClickHouseClientFn = async (
  params: QueryParams,
) => {
  return clickhouse.query({
    query: params.query,
    format: params.format ?? "JSONEachRow",
    query_params: params.query_params,
    clickhouse_settings: params.clickhouse_settings,
  });
};

const qp = new QueryPanelSdkAPI(
  secrets.queryPanelApiUrl,
  secrets.queryPanelPrivateKey,
  secrets.queryPanelWorkspaceId,
);

qp.attachClickhouse("product_analytics", executeClickHouse, {
  database: secrets.clickhouseDatabase,
  description: "Tenant-scoped product usage and API request analytics",
  allowedTables: ["product_events", "api_requests"],
  tenantFieldName: "organization_id",
  tenantFieldType: "String",
  enforceTenantIsolation: true,
});

await qp.syncSchema("product_analytics", {
  tenantId: "schema-sync",
});

getAnalyticsSecrets() represents your server-only Vault wrapper; in this repository, secrets are resolved through Supabase Vault's get_secret RPC rather than stored in environment variables or source. Run schema sync during a controlled setup or deployment workflow, not on every customer request. Keep the ClickHouse account read-only for analytical execution. QueryPanel's allowlist narrows what the adapter can introspect and query; it does not replace database permissions.

Your authenticated API route can then ask a customer-scoped question:

const result = await qp.ask(
  "Show API error rate by endpoint for the last seven days",
  {
    database: "product_analytics",
    tenantId: auth.organizationId,
    pipeline: "v2",
    chartType: "vizspec",
    maxRetry: 2,
    debug: false,
  },
);

return {
  rationale: result.rationale,
  chart: result.chart,
  rows: result.rows,
  sessionId: result.querypanelSessionId,
};

auth.organizationId must come from verified backend auth, not request JSON or a URL parameter. Leave debug false for customer-facing responses so rationales stay in business language; reserve engineer-style schema detail for internal tooling.

For the wider generation and repair model, use the production NL-to-SQL checklist. This article stays focused on the ClickHouse serving path.

Design tenant isolation as an invariant

There is no single universal ClickHouse tenancy layout. Shared tables with a tenant column, tenant-specific databases, and isolated clusters can all be valid. The invariant is simpler: every route, saved view, export, follow-up question, and cache lookup must preserve the server-verified tenant.

Use these controls together:

  1. Read-only database identity. The analytics execution user should not be able to mutate source tables or bypass controls by copying data.
  2. Table allowlist. Expose only the modeled tables needed for customer analytics.
  3. Server-derived tenant context. Pass a trusted tenant ID into qp.ask().
  4. Database controls where appropriate. ClickHouse row policies filter rows a user can read. Its documentation also warns that row policies make sense for read-only users because write access can defeat the restriction.
  5. Two-tenant regression tests. Seed tenants with unequal row counts and overlapping labels. Equal test data can hide an unscoped aggregate.
  6. Tenant-aware caches. Include tenant identity, metric definition, filters, and relevant authorization scope in every result-cache key.

If you need the broader authorization model, read Tenant Isolation for Customer-Facing Analytics. Do not claim isolation is complete until you have tested exports, dashboard copies, AI follow-ups, and errors as well as the first chart load.

Control query cost and noisy neighbors

A valid ClickHouse query can still be a bad product query. One tenant asking for an unbounded time range or high-cardinality breakdown can consume resources needed by everyone else.

Put limits in the execution environment and verify them under load:

  • maximum execution time
  • maximum rows or bytes read
  • maximum result rows and bytes
  • memory limits
  • concurrent-query admission controls
  • cancellation when the browser or API request disconnects
  • bounded retries so a failing query does not multiply load

ClickHouse distinguishes per-query complexity restrictions from quotas, which account for resource use across a set of queries over time. Quotas can track or restrict requests, rows, bytes, execution time, and other consumption. Choose settings from measured traffic rather than copying a vendor benchmark.

Also measure dashboard fan-out. A page with 12 charts is not one database request. If 200 customers refresh it after receiving the same email, the burst can be 2,400 queries before retries, drilldowns, or exports.

Model for repeated customer questions

ClickHouse works best when the physical data design reflects the access pattern. Start with the questions the product promises to answer:

  • Which tenant and time columns appear in almost every query?
  • Which filters are selective?
  • Which dimensions have high cardinality?
  • Which aggregates repeat on every dashboard load?
  • How quickly must late events or corrections appear?

Use raw or lightly modeled event tables while the question set is still changing. Add pre-aggregations when repeated work is measurable. ClickHouse's incremental materialized view documentation explains that incremental views shift computation from query time to insert time and process newly inserted blocks. That is useful for stable metrics such as hourly request counts, but it makes the target schema and aggregation semantics part of your product contract.

Do not copy an OLTP schema into ClickHouse unchanged and assume columnar storage fixes every join. Conversely, do not denormalize every dimension before you know which customer interactions are latency-sensitive. Measure, then shape the hot paths.

What real-world evidence does—and does not—prove

The strongest public results are workload-specific. In an AWS case study about SEON, AWS reports that database performance for part of SEON's fraud-screening workload was up to 80% faster in some instances after working with ClickHouse on AWS. The same case study describes worst-case traffic testing with a customer.

That is useful evidence for two ideas: analytical engine choice can materially change a high-volume product workload, and load testing should reflect adverse traffic. It is not a promise that every dashboard will improve by 80%, and it is not a QueryPanel result. Your data shape, cluster, queries, concurrency, and previous system determine the outcome.

A realistic ClickHouse embedded analytics POC

Use production-shaped data and define pass thresholds before the run. The values below are a template; replace them with your product SLOs.

TestSetupMeasureExample pass signal
Tenant isolationTwo tenants with unequal volumes and overlapping event namesSQL, row counts, exports, saved views, follow-upsNo cross-tenant rows in any path
Dashboard fan-out8–12 representative charts loaded togetherPage p95, query p95, errors, queue timeMeets the product SLO without retry storms
Burst concurrencyExpected peak tenants refresh simultaneouslyThroughput, p95/p99, memory, rejected queriesGraceful admission control; healthy tenants remain responsive
Live ingestionNormal ingest rate continues during dashboard testsQuery latency plus ingest lagNeither path breaches its SLO
Broad explorationLargest allowed date range and cardinalityRows/bytes read, memory, timeout behaviorQuery completes inside its budget or fails clearly
CancellationClose the route during a slow requestServer and database work after disconnectWork is cancelled; resources are released
Schema driftAdd, rename, or remove a non-critical column in stagingSync and generated-query behaviorDrift is detected; no silent wrong answer
Bad generationForce invalid syntax or a disallowed table in stagingValidation, retry count, user errorNo unsafe execution; bounded repair and useful error

Record cache state and warmup separately. Run the same query mix cold, warm, and during ingestion. ClickHouse's guidance recommends representative queries and production-like conditions because a selective query and a broad aggregation have very different resource profiles.

Where QueryPanel fits

QueryPanel's headless Node SDK is the primary fit for ClickHouse teams that already own their frontend and want the backend to remain the execution boundary. Your application supplies the ClickHouse callback, resolves the tenant, chooses the UI, and can add database settings or observability around each request. QueryPanel supplies schema-aware, dialect-aware question-to-SQL and chart generation on that controlled path.

This is a good fit when:

  • customer analytics is a core product feature rather than a separate BI portal
  • ClickHouse already serves product events, API usage, telemetry, or other analytical data
  • the team needs tenant-aware natural-language questions and charts
  • engineers want to inspect and constrain the execution path
  • the frontend must follow an existing design system

It is not a substitute for ClickHouse modeling, capacity planning, database access control, or a defined metric contract.

If you want a managed dashboard workspace instead of building the interface, QueryPanel also offers a headful React SDK with a Notion-like dashboard experience and AI-assisted tenant customization. For this implementation, however, the headless path keeps the ClickHouse integration and UI contract directly in your engineering team's hands.

FAQ

Is ClickHouse good for customer-facing embedded analytics?

Yes, especially for event-heavy, time-series, usage, log, and telemetry workloads that need interactive aggregation. The result still depends on physical design, tenant controls, query limits, concurrency, and the product layer around the database.

When should a SaaS product move analytics from Postgres to ClickHouse?

Consider a separate analytical serving layer when customer queries compete with transactional work, event volume outgrows practical Postgres rollups, freshness requirements tighten, or concurrent exploration becomes a core feature. For a cross-database decision, use the database guide for SaaS embedded analytics.

How do you enforce tenant isolation in ClickHouse dashboards?

Resolve the tenant on the backend, use read-only database identities, restrict tables, apply ClickHouse row policies or stronger physical isolation where appropriate, and regression-test every output path with two unequal tenants. Frontend filters are not an authorization boundary.

Do ClickHouse dashboards need materialized views?

Not always. Materialized views help when the same expensive aggregation runs repeatedly and its semantics are stable. Start with measured query profiles; add a view when shifting compute to ingestion improves a known hot path without making correctness or backfills harder.

How do you prevent noisy-neighbor queries?

Combine bounded date ranges and result sizes with ClickHouse query settings, quotas or workload controls, cancellation, limited retries, and concurrency tests. Monitor per-tenant consumption so one broad question cannot silently degrade every customer's dashboard.

Can natural-language-to-SQL safely query ClickHouse?

It can when the system uses ClickHouse-specific generation, schema grounding, a table allowlist, server-verified tenant context, validation before execution, and database-enforced resource limits. A prompt instruction alone is not a complete security boundary.

How does QueryPanel connect to ClickHouse?

The Node SDK's attachClickhouse method accepts a callback compatible with the official ClickHouse JavaScript client's query parameters. Your backend owns the client and credentials, and qp.ask() sends generated SQL back through that callback for validation and execution.

Should I use QueryPanel's headless or headful SDK with ClickHouse?

Use the headless Node SDK when you want a custom UI, backend-controlled execution, and direct control over ClickHouse settings and observability. Use the headful React SDK when launch speed and a ready-made, customizable dashboard workspace matter more than owning every frontend interaction.


Build the first ClickHouse analytics path around one real tenant-scoped dataset and five questions customers already ask. Use QueryPanel's headless Node SDK to keep the UI and execution boundary under your control, then promote it only after the isolation, concurrency, and failure tests pass.