@hypequery/clickhouse

Type-safe ClickHouse query builder and schema generator.

Latest: 2.15.0 · November 9, 2026 · npm · CHANGELOG.md

RSS
2.15.0Minor

Minor changes

  • Aggregate result types now match what ClickHouse returns, instead of string for every aggregate:

    • sum(): number for Float* and Decimal columns, string for integer columns (they widen to a quoted Int64), plus | null for Nullable columns. Over a subquery or CTE column known only as number, it is number | string.
    • avg(), stddev(), variance(): number | null. NaN over too few rows arrives as null.
    • min(), max(), argMin(), argMax(): the column's own type.
    • quantile(): number | null for numeric columns, the column's type for dates.
    • count(), countDistinct(), approxCountDistinct(): unchanged (string).

    Code that converted results with Number(...) keeps compiling. Code that called string methods on, for example, a Float64 sum now gets a type error where it used to fail at runtime.

    Schemas can now declare AggregateFunction(...) and SimpleAggregateFunction(fn, T) columns, so a generated schema containing an AggregatingMergeTree table type-checks with createQueryBuilder<IntrospectedSchema>(). A SimpleAggregateFunction(fn, T) column has T's type; an AggregateFunction column is an opaque state that you read with mergeState().

    GROUP BY inference no longer adds a selected expression that wraps an aggregate (round(avg(x), 2)) or a window function (rank() OVER (ORDER BY sum(x))), which ClickHouse rejected with "Aggregate function ... is found in GROUP BY".

    The readonly error for the precision-safe 64-bit setting now names the fixes that keep precision: setting output_format_json_quote_64bit_integers = 1 in the user's profile, or readonly = 2. integerJsonEncoding: 'server-default' is still offered as the fallback.

    The legacy hypequery-generate-types generator types parameterized views, and types SimpleAggregateFunction columns by their value type, as @hypequery/cli now does.

    c320820
  • Stream results as Arrow. builder.streamArrow() and db.rawArrowStream(sql, params) return the result as Arrow IPC stream bytes (ClickHouse ArrowStream) with native column types, for any Arrow reader to decode or for passing on unchanged. Batches are uncompressed by default, because JavaScript Arrow readers don't include the LZ4 codec; a connection-level or per-call output_format_arrow_compression_method overrides that. db.rawStream(sql, params) streams a raw statement's rows, as builder.stream() does for built queries. If ClickHouse fails after sending part of an Arrow result, the stream errors rather than handing the exception text to the decoder: with ClickHouse's own error on ClickHouse 25.11 or later, which marks the failure, and on older servers because the bytes end without Arrow's end-of-stream marker. Custom adapters can implement the new optional DatabaseAdapter.streamArrow.

    3bb485c
  • Fix JavaScript Date filter values against DateTime sorting-key columns. A Date used to render as a quoted ISO string ('2026-03-01T00:00:00.000Z'), which ClickHouse rejects with TYPE_MISMATCH when it compares the string with a DateTime column in the sorting key. The same happened for a Date column, and for any value with milliseconds. A Date now renders as toDateTime('2026-03-01 00:00:00', 'UTC'), or as toDateTime64(..., 3, 'UTC') when it has milliseconds or falls outside the DateTime range. This keeps the exact UTC instant, still lets the primary index prune, and works in IN lists. It applies to where, between, in, rawQuery placeholders and CrossFilter. CrossFilter no longer converts Date values to ISO strings itself. Dates inside array, tuple and map parameters are still sent as ISO text, which CAST parses correctly.

    c320820

Patch changes

  • Arrow streams work for readonly = 1 users and catch mid-stream failures on servers before ClickHouse 25.11.

    • streamArrow() and rawArrowStream() request the format with a FORMAT ArrowStream clause instead of the default_format setting, which readonly = 1 users may not change. A trailing ; or comment is dropped first.
    • The uncompressed-batches default (output_format_arrow_compression_method = 'none') is still sent, and a readonly = 1 user accepts it when its profile already sets that value. When ClickHouse rejects it, the error explains the fix. The new arrowCompression: 'server-default' adapter option leaves the setting out, so batches use the server's compression method.
    • Detecting a mid-stream failure from its tagged exception block needs ClickHouse 25.11 or later, because older servers send no X-ClickHouse-Exception-Tag. An Arrow stream from any server now also errors when its bytes end without Arrow's end-of-stream marker, so a failed or truncated stream no longer looks complete. On older servers, ClickHouse's error message is recovered from the response where possible.
    0ba4596
  • stream() no longer hangs when an adapter's byte stream splits a JSON row across two chunks, keeps a final row that has no trailing newline, and keeps characters split across chunks intact. The built-in adapter was unaffected because its client hands over whole rows; custom adapters that stream raw bytes were.

    3bb485c
2.14.0Minor

Minor changes

  • Add toSQLWithPlaceholders(placeholder) to the query builder: it compiles the query and writes each bound value through the callback, in SQL order, so a caller can number placeholders when embedding the statement in a larger one. @hypequery/datasets adds it as an optional QueryBuilderLike member, used only by datasets dialects that number placeholders. ClickHouse datasets queries keep using toSQLWithParams, so existing builders and generated SQL are unchanged.

    bc14e60
  • Add measure.merge(stateFunction, field) (and merge() for metric configs) to read AggregateFunction columns of AggregatingMergeTree tables with the matching -Merge combinator, for example measure.merge('uniq', 'users') renders uniqMerge(users). Trailing and to-date windows merge states across buckets, so weekly distinct counts from daily states are exact to the state function. State functions are allowlisted; merges of estimating states are catalogued as approximate. Merge measures are refused with measure filters, raw sql, relationships, cumulative windows, semantic backends and deployment-contract publishing. @hypequery/clickhouse adds mergeState(stateFunction, column, alias) to the query builder; QueryBuilderLike gains it as an optional member.

    4af1695

Patch changes

  • Upgrade dotenv to v18 and load env files quietly, so dotenv's "injected env" log lines no longer appear in CLI output.

    1570a03
2.13.0Minor

Minor changes

  • Support non-empty arrays of typed column pairs in innerJoin, leftJoin, leftAnyJoin, rightJoin, and fullJoin for composite-key equality joins to schema tables and typed CTEs. Preserve existing single-pair calls, alias handling, and literal ON filters.

    ccb2577

Patch changes

  • Fix the CLI declaration re-export to use an explicit .js extension, so importing the package root or CLI entry point passes NodeNext and Node16 type checking with skipLibCheck: false and preserves schema-generator types.

    0d593a3
2.12.2Patch

Patch changes

  • Expose a HypeQuery-owned ClickHouse settings union that accepts settings from the Node, web, and legacy common clients without widening accepted setting values.

    5ce2ac8
  • Refresh production dependencies. Keep the ClickHouse adapter compatible with the client packages' bundled setting types.

    40ac480
2.12.1Patch

Patch changes

  • Keep select() and selectConst() type checking linear on wide tables.

    String aliases were validated by expanding every column into a `${column} ${'as' | 'AS' | 'As' | 'aS'} ${string}` union, so a table with ~1,000 columns produced tens of thousands of template literal types and exhausted the compiler. The constraint now accepts any x as y pattern and each selection is checked on its own, so unknown columns and unknown aliased columns are still rejected. A bad column in select([...]) now reports "No overload matches this call" rather than pointing at the element.

    bb14fd8
2.12.0Minor

Minor changes

  • Add approxCountDistinct (RFC 0015, HQ-77):

    • measure.approxCountDistinct(field) and the approxCountDistinct aggregation helper estimate distinct counts with ClickHouse uniq, including with measure filters.
    • The ClickHouse query builder gains .approxCountDistinct(column, alias?).
    • Catalogs, the semantic contract, and the agent-safe catalog mark approximate measures, and derived measures that use them, with approximate: true.
    • The in-memory backend computes the count exactly.
    • Publishing to Cloud refuses approximate measures with an actionable error until deployment contract 3 is emitted.
    091b8a6

Patch changes

  • Add minute and hour time grains (RFC 0015, HQ-77). They bucket with toStartOfMinute/toStartOfHour and appear in catalogs, semantic query schemas, and the local MCP tools.

    A new timeGrains dataset option restricts the grains a dataset supports, for example to refuse sub-day buckets on a Date time key. Queries, .by(), and the catalog honor it.

    Publishing to Cloud still emits deployment contract 2:

    • it refuses a sub-day defaults.timeGrain or pinned metric grain with an actionable error;
    • it refuses dataset-level timeGrains restrictions it cannot preserve;
    • published metric grain lists and rehydrated datasets stay at day through year.
    c48b1b0
2.11.2Patch

Patch changes

  • Group a condition's column so a trusted SQL expression cannot widen the predicates beside it.

    A dataset dimension may be backed by SQL its author wrote during a build, and the semantic layer passes that expression into the column slot of a where. The tenant predicate is a separate where on the same query, so the two are joined with AND. Rendered without grouping, an expression containing a top-level OR rebound across that AND: a dimension of active OR is_public produced tenant_id = ? AND active OR is_public = ?, and because AND binds tighter than OR, the second branch matched rows belonging to every tenant.

    groupConditionColumn now wraps such an expression into one operand and terminates a trailing line comment, which unterminated would swallow the rest of the statement — the tenant predicate included. An ordinary column is unchanged, so no existing SQL moves.

    The package already applied both rules to raw expression nodes; this extends them to the column slot of a condition, which is the path the semantic layer uses.

    3e34e07
2.11.1Patch

Patch changes

  • Remove the unused internal CompiledQueryV1 execution contract and its exports. There is no change to ClickHouse query construction or execution; existing queries still use positional ? placeholders and the established escaping path.

    2910bf0
2.11.0Minor

Minor changes

  • Add recursive CTEs, typed CTE sources, and raw CTE parameters.

    • Use withRecursiveCTE() to write recursive queries. Ordinary CTEs and scalar aliases can share the WITH RECURSIVE clause.
    • Use db.withCTE(...).table(alias) or db.withRecursiveCTE(...).table(alias) to read from a CTE with typed columns. FINAL and PREWHERE are rejected on CTE sources.
    • Pass { sql, parameters } to either method to bind {name:Type} placeholders. Values retain their declared types, including UUIDs, arrays, tuples, maps, and JSON objects. Values are escaped client-side at rendering or execution time.

    Existing withCTE() calls remain supported.

    dd8acd3
  • Fix two long-standing CTE gaps in the query builder.

    CTE aliases are now typed join targets. withCTE('alias', builder) derives the alias's columns from the builder passed in, and withCTE('alias', sql, columns) takes a column declaration for a raw SQL body. Joining the alias, selecting alias.column, and filtering on it are all checked, and the column types flow through to the result row. A raw CTE with no declared columns behaves as before: it renders, but its alias is not a typed join target. Joining a CTE does not accept the trailing table-alias argument, which resolves through the schema.

    CTE values also stay bound. withCTE previously rendered a builder subquery with toSQL(), escaping its values into the CTE string while the rest of the query used bound parameters. The CTE body now keeps its placeholders and contributes its parameters ahead of the outer query's. Rendered SQL is unchanged, as is the ctes array on the deprecated getConfig().

    89e0619
2.10.1Dependency update: @hypequery/protocol@0.13.0
2.10.0Minor

Minor changes

  • Propagate semantic query cancellation through the datasets client and ClickHouse semantic backend, and enforce MCP query deadlines and serialized-response byte ceilings with stable budget error classifications.

    916eef3

Updated dependencies: @hypequery/protocol@0.12.0

2.9.1Dependency update: @hypequery/protocol@0.11.1
2.9.0Minor

Minor changes

  • Generate valid TypeScript for pretty-printed ClickHouse types and infer named Tuple(...) values as objects in generated records, query results, and inserts.

    DESCRIBE TABLE returns wide types pretty-printed across several lines. Those were embedded in single-quoted string literals, so any table with a multi-line type produced a schema file that did not compile. Type literals are now serialized with JSON.stringify, which escapes newlines and quotes together.

    Named tuples are also inferred structurally instead of positionally, matching what ClickHouse actually returns over JSONEachRow. For a column typed Array(Tuple(installed_version String, path Nullable(String))), the record interface the CLI writes changes from:

    Potentially breaking for @hypequery/clickhouse. InferClickHouseType previously resolved a named tuple to a positional tuple whose elements were never. Positional reads compiled, because never is assignable to anything, but carried no type information. They are now property accesses:

    InsertValue changes the same way, but nothing that typechecked before stops typechecking: its named-tuple elements were also never, so no value could satisfy them. Named-tuple columns previously could not be inserted without a cast, and can now be written as objects:

    Object inference assumes the server serializes named tuples as JSON objects (output_format_json_named_tuples_as_objects, on by default). Connections that disable it, or that pin compatibility to a release predating the default, still receive arrays.

    Regenerating with the CLI is what surfaces the new record types; existing generated files keep their current shape until you re-run hypequery generate.

    40eacb9
2.8.1Patch

Patch changes

  • Add explicit support for ClickHouse connections running under readonly = 1.

    The adapter sent output_format_json_quote_64bit_integers: 1 with every query. That preserves integers beyond JavaScript's safe range and matches HypeQuery's generated string types. However, readonly = 1 rejects the setting when it differs from the user's profile, so queries can fail with:

    Cannot modify 'output_format_json_quote_64bit_integers' setting in readonly mode.
    

    Two changes keep that tradeoff explicit:

    • Strict read-only mode. Set integerJsonEncoding: 'server-default' on createQueryBuilder or ClickHouseAdapter to omit the adapter-owned setting. This performs no capability probe or retry. The default remains 'quoted' because server-default JSON numbers can lose precision beyond 2^53 and may not match generated Int64/UInt64 string types. If the default is rejected, the adapter now returns an actionable error describing this option.
    • Connection settings now outrank the adapter's default. Precedence is adapter default < connection clickhouse_settings < per-query settings. Previously the adapter applied its own value last for this flag, so a connection-level value could not override it. Caller-owned values are never replaced or reinterpreted by the adapter.
    6c02ad6
2.8.0Minor

Minor changes

  • Support AbortSignal on query, stream, and insert execution.

    execute(), stream(), streamForEach(), rawQuery(), and insert().execute() now accept an abortSignal that is forwarded to the ClickHouse client, so callers can cancel in-flight requests (e.g. when an HTTP request is aborted or a component unmounts).

    Deduplicated executions stay deduplicated: callers of the same cache key share one query, each caller's abort rejects only that caller, and the shared query is cancelled once no waiter is left. Background stale-while-revalidate refreshes keep running after the caller that triggered them aborts.

    Cancellation stays active across the whole request lifecycle: an already-aborted signal rejects before any HTTP request or cache write happens, aborting while a result body is still being read rejects with the signal's reason and closes the result set, and aborting mid-stream errors the consumer stream and destroys the underlying connection stream.

    ef890a3
2.7.0Minor

Minor changes

  • Add close() for graceful shutdown.

    createQueryBuilder(...) now returns a close() method that releases the underlying ClickHouse connection pool, so processes can drain and exit without dangling keep-alive sockets. DatabaseAdapter gained an optional close() that custom adapters can implement; adapters without it resolve as a no-op.

    A client supplied through createQueryBuilder({ client }) is closed as well, so only call close() when the builder owns the process lifetime of that client.

    d943a98
2.6.0Minor

Minor changes

  • Add type-safe subqueries in the FROM clause with db.from(queryBuilder), preserving nested parameters and exposing only the subquery output columns to the outer builder.

    309a971
2.5.6Dependency update: @hypequery/protocol@0.11.0
2.5.5Patch

Patch changes

  • Refresh every npm package page with a concise README and complete HypeQuery homepage and repository metadata.

    e370da0

Updated dependencies: @hypequery/protocol@0.10.2

2.5.4Dependency update: @hypequery/protocol@0.10.1
2.5.3Dependency update: @hypequery/protocol@0.10.0
2.5.2Patch

Patch changes

  • Support bigint SQL parameters and add regression coverage ensuring injection- shaped strings nested inside arrays and objects remain contained within their ClickHouse string literals.

    4a1d4e3
2.5.1Patch

Patch changes

  • Fix AND/OR precedence for logical groups in WHERE/PREWHERE/HAVING (#348). expr.or(...) / expr.and(...) groups are now parenthesized as a whole, so an OR group ANDed alongside other conditions (e.g. a tenant scope) can no longer rebind and escape those conditions. Raw fragments containing a top-level AND/OR are also wrapped when combined with sibling conditions, including multiple having() fragments. The scanner now follows ClickHouse quoting, heredoc, delimiter, and comment rules, and trailing line comments are safely terminated before generated SQL is appended. This closes data-scoping holes where OR branches or comments could silently bypass sibling filters.

    Harden the legacy positional-parameter renderer without changing the trusted raw-SQL escape hatches. JSON-serialized values now pass through ClickHouse string escaping, positional ? markers are recognized only in SQL code (not quotes, heredocs, or comments), placeholder mismatches fail closed, and non-finite numbers or invalid dates are rejected. CTE aliases and predicate function names must now be valid unquoted identifiers.

    2879161
2.5.0Minor

Minor changes

  • Add CompiledQuery v1 (RFC 0010) beside the legacy positional query path (CH-01).

    Introduces @hypequery/clickhouse exports for a versioned execution-request contract: named typed parameters bound to native server placeholders (values never render into SQL text), a closed operation set (query/command/insert), a bounded settings allow-list, an authoritative query id plus optional correlation id, caller-vs-policy deadline precedence, a redacted non-executable debug form, and the stable public error envelope with a closed category set. Parameter values reuse the RFC 0001 tagged-value contract from @hypequery/protocol.

    Additive and non-breaking: the legacy adapter.query(sql, params[]) path and substituteParameters/escapeValue are unchanged. Wiring the built-in @clickhouse/client {name:Type} + query_params transport is a follow-up (CH-02).

    ad6e45e
2.4.0Minor

Minor changes

  • Add analytical dataset measures for percentiles, medians, argMax/argMin, sample standard deviation, and sample variance. The measures execute through the ClickHouse builder, ClickHouse semantic backend, and in-memory backend, and are surfaced in dataset catalogs and semantic contracts.

    379998a
2.3.0Minor

Minor changes

  • Add type-safe insert API: db.insert(table).values(rows).execute().

    Row shapes are derived from the schema — Nullable(...) columns are optional, every other column is required, and value types are checked at compile time (DateTime columns accept string | Date | number, Date columns take 'YYYY-MM-DD' strings, 64-bit integers accept string | number | bigint). Use .columns([...]) to insert a subset of columns and let ClickHouse fill table DEFAULTs, and .settings({...}) for per-insert ClickHouse settings. Empty batches are a no-op ({ executed: false }), and non-finite numbers are rejected before the request is sent. Inserts run through the native client insert path (JSONEachRow); DatabaseAdapter gains an optional insert method, so existing custom adapters keep working unchanged. Adapter authors can implement insert support in a few lines with the new buildJsonEachRowInsert helper, which renders the complete INSERT ... FORMAT JSONEachRow statement with identical row normalization and settings.

    Behavior change for reads: the built-in ClickHouse adapter now sets output_format_json_quote_64bit_integers: 1 by default, so Int64/UInt64 (and larger) values are returned as strings instead of lossy numbers — matching what the generated schema types already declare. If you were relying on numeric 64-bit values at runtime, either convert with Number(...)/BigInt(...) or pass output_format_json_quote_64bit_integers: 0 via query settings to restore the old behavior.

    0264357
2.2.0Minor

Minor changes

  • Relationship-aware semantics: to-one relationships are now queryable end to end, and every metadata surface advertises them.

    Querying (@hypequery/datasets, @hypequery/clickhouse): dataset and metric queries can select, filter, and order by to-one related fields one hop deep (dimensions: ['customer.country']). Traversal executes as a ClickHouse LEFT ANY JOIN (new leftAnyJoin query-builder method), so base rows survive, duplicate target join keys can never fan out aggregates, and production matches the in-memory backend's first-match semantics. When runtime tenancy is active, joined targets with a tenantKey are scoped inside the join condition. hasMany remains metadata-only, and result row types include qualified fields (row['customer.country'] is typed).

    Metadata: the catalog and semantic contract expose queryable and fields per relationship (replacing execution: 'metadata_only'; SEMANTIC_CONTRACT_VERSION is now 2). Generated agent tools, Serve's Zod/OpenAPI input schemas, and MCP get_dataset_schema all advertise the same qualified field names the validators accept — including for config-shaped datasets in MCP. Serve no longer advertises dimensions as filterable when a dataset explicitly declares filters: {}.

    Validation: dataset() now rejects relationship names that match the dataset's source table (the join alias would shadow the base table) or contain dots — both configurations previously failed confusingly at query time.

    Deprecations (no removals, no behavior changes): the plan/backend execution path is frozen in favor of createDatasetClient({ queryBuilder }). createBackend, the backend client option, createInMemoryBackend, and the PlanNode/SemanticBackend protocol exports are @deprecated and receive bug fixes only.

    b6b5111
2.1.3Patch

Patch changes

  • Harden logging and diagnostics without changing public APIs: never mark authenticated or tenant-aware responses as publicly cacheable, log the parameterized SQL template instead of a value-substituted string, and redact connection URLs in CLI output and error messages.

    688a9e2
2.1.2Patch

Patch changes

  • Export substituteParameters and escapeValue from the package entrypoint so third-party adapters can reuse the same SQL parameter rendering as the built-in ClickHouse adapter.

    6efb2e3
2.1.1Patch

Patch changes

  • Widened the @hypequery/datasets peer dependency range to >=0.1.0 so future datasets releases no longer force a clickhouse version bump.

Updated dependencies: @hypequery/datasets@0.3.0

2.1.0Minor

Minor changes

  • Add the ClickHouse backend for @hypequery/datasets under the new @hypequery/clickhouse/datasets export. createBackend translates semantic plans into ClickHouse queries, including dimensions, aggregations, derived metrics, filters, ordering, pagination, and tenant predicates.

    Type generation can now return definitions without writing a file, accept an injected client, and recognizes additional ClickHouse types and nested wrapper syntax. SQL literal and interval rendering is also hardened so generated expressions safely reject malformed interval input and escape string values.

    75349dd
2.0.2Patch

Patch changes

  • SECURITY: Fixed SQL injection vulnerability in parameter escaping. The escapeValue() function in src/core/utils.ts now properly escapes backslashes before single quotes, preventing attackers from using trailing backslashes to break out of string literals and inject arbitrary SQL. ClickHouse recognizes both SQL-standard ('') and C-style (\) escape sequences—a trailing backslash could escape the closing quote, allowing the second parameter to execute as raw SQL instead of being treated as a string literal. All users should upgrade immediately.

2.0.1Patch

Patch changes

  • Fix aggregation GROUP BY inference when chaining aggregate helpers without an explicit grouping dimension. Aggregate aliases such as revenue from SUM(price) AS revenue are no longer reused as inferred GROUP BY keys, which avoids invalid SQL like GROUP BY revenue when additional aggregations are appended.

    890c76d
2.0.0Major

Major changes

  • Release @hypequery/clickhouse as 2.0.0 and include the related CLI patch release.

    For @hypequery/clickhouse, this release includes:

    • a refactor toward an explicit query-node internal model
    • stricter withRelation() behavior for chained relationships
    • stricter tuple IN validation and improved empty-set filter semantics
    • additive groupBy() behavior and improved aggregation inference
    • support for ClickHouse-native builder features such as arrayJoin(), leftArrayJoin(), limitBy(), and withTotals()
    • exported built-in time-bucketing helpers such as toStartOfMinute() through toStartOfYear()
    • url as the preferred ClickHouse connection field, while keeping deprecated host compatibility

    For @hypequery/cli, this release includes:

    • stricter non-interactive setup behavior with cleaner failure paths
    • NodeNext-safe generated scaffold imports
    • improved scaffold dependency installation, including zod and aligned canary sibling versions
    • support for --skip-connection during init scaffolding
    29761f3
  • query builder internals are now centered on a structured SelectQueryNode model instead of looser config mutation. This keeps the public builder workflow largely the same, but makes filtering, relation application, validation, and SQL compilation more explicit internally.

  • advanced builder inspection should now prefer getQueryNode(). getConfig() still exists for compatibility, but it should be treated as a legacy inspection helper rather than the main public view of builder state.

  • withRelation() behavior is stricter and better defined:

    • string relationship lookup still works for reusable runtime registry usage
    • direct JoinPath usage is the typed path for compile-time table and alias widening
    • alias override is only supported for single-step relationships, not chains
  • tuple IN handling is stricter:

    • single-column tuple IN is now covered explicitly
    • tuple width is validated against the selected column width
    • malformed tuple input fails earlier with clearer errors
  • add first-class isNull / isNotNull filter operators.

Breaking changes

  • builder chains should be treated as immutable. If you are composing a query conditionally, reassign the returned builder rather than assuming methods mutate the existing instance.

  • withRelation(..., { alias }) no longer supports alias override for chained relationships. Define aliases on each step of the chain instead.

  • tuple IN filters now fail earlier when the tuple width does not match the target column list.

Additional changes

  • docs, examples, and scaffolding now prefer url over host for ClickHouse connection config.

  • host remains supported as a deprecated backward-compatible option. Existing host-only configs should continue to work.

  • adapter namespace derivation now reads either url or host, which keeps cache/namespace behavior stable across both config styles.

  • fix several query-builder correctness issues around grouping, aggregation inference, and empty-set filter behavior:

    • NOT IN [] / GLOBAL NOT IN [] now compile to 1 = 1 instead of 1 = 0
    • repeated groupBy() calls are additive and de-duplicated
    • aliased selected expressions are grouped correctly when aggregations are added later
    • explicit groupBy() clauses are preserved without duplicate inferred entries
    • aggregation helpers now accept qualified joined columns in their type surface
  • add ClickHouse-specific builder support for:

    • arrayJoin()
    • leftArrayJoin()
    • limitBy()
    • withTotals()

    This extends the query AST, SQL renderer, type surface, and integration coverage for several common ClickHouse-native query patterns.

  • tighten arrayJoin() and leftArrayJoin() typing so they only accept array-typed columns, including qualified joined columns and aliased joined columns.

  • export the built-in start-of time helpers from the package entrypoint:

    • toStartOfMinute
    • toStartOfHour
    • toStartOfDay
    • toStartOfWeek
    • toStartOfMonth
    • toStartOfQuarter
    • toStartOfYear
1.6.2Patch

Patch changes

  • Fix tuple type inference in generated types to properly render positional tuple types instead of falling back to unknown. This includes:

    • Added ParseTopLevelArgs type helper to correctly parse comma-separated type arguments while preserving nested parentheses
    • Updated InferClickHouseType to handle Tuple(...) types by inferring positional TypeScript tuple types
    • Refactored CLI type-parsing logic to extract clickhouseToTsType into a separate module with improved tuple handling
    • Added support for nested tuple types within Array, Map, and Nullable wrappers
    • Added test coverage for tuple type inference scenarios
    f87df2f
1.6.1Patch

Patch changes

  • Fix two ClickHouse query/type-safety issues.

    • Fix builder-level per-query settings so .settings(...) is forwarded at execution time via ClickHouse clickhouse_settings instead of being silently ignored. This also forwards queryId as ClickHouse query_id, merges repeated .settings(...) calls, and adds matching rawQuery(..., options?) support for per-query execution options.
    • Fix type inference for wide ClickHouse integers so Int64/UInt64 and wider widths map to string instead of number in both core inference and generated record types. This also preserves rawAs() aliases in mixed select([...]) calls so aliased raw expressions appear in execute() result types with unknown by default or the explicit generic type when provided.
    1fad488
1.6.0Minor

Minor changes

  • Add typed scalar WITH support for ClickHouse and improve expression ergonomics.

    • add withScalar(alias, expr => ...) to emit scalar WITH <expression> AS <alias> clauses
    • enforce scalar alias validation for safe unquoted SQL identifiers
    • add ClickHouse predicate helper namespace expr.ch, including expr.ch.dictGet(...)
    • expand unit + type test coverage for scalar aliases and typed helper usage
    • document scalar WITH aliases in query-building docs (serve + standalone)
    cc466d5
1.5.0Minor

Breaking changes

  • DateTime type mapping correction: DateTime, DateTime64, Date, and Date32 columns are now correctly typed as string instead of Date. This matches the actual runtime behavior of @clickhouse/client when using JSON output formats (like JSONEachRow), which return DateTime values as strings to preserve sub-millisecond precision that JavaScript Date objects cannot represent.

    Migration guide:

    Why this change: Previously, TypeScript indicated these fields were Date objects, but at runtime they were actually strings. This caused type mismatches and runtime errors. The fix surfaces this at compile time, preventing bugs.

1.4.0Minor

Features

  • add an experimental caching layer that plugs into every execute() call: supports cache-first, network-first, and stale-while-revalidate modes, per-query overrides, tag invalidation, in-flight dedupe, cache-aware logging metadata, and a CacheController API for warming or inspecting hit stats. Ships with a memory LRU provider plus serialization helpers and provider hooks for custom stores.

  • expand the example dashboard with a cache demo page (/cache), refresh/invalidate buttons, warming + stats API routes, and environment toggles so developers can see cache hits/misses/stale hits in real time.

Fixes / improvements

  • ensure cached entries default cacheTimeMs to ttlMs + staleTtlMs, fix namespace parsing in the memory provider so tag invalidation works even when the host contains a protocol, and run mergeCacheOptionsPartial/initializeCacheRuntime helpers to keep query-builder lean.

  • simplify the example dashboard configuration by removing the Upstash fallback, documenting provider requirements (tag hooks/TTL handling), and adding /api/cache/* endpoints to warm caches and inspect hit rates.

  • make npm run test fast again (unit + type tests only) while moving integration tests behind npm run test:integration, and streamline withRelation so chained relationships reuse a single join applier.

1.3.2Patch

Features

  • teach expressions and select clauses their result types, so aliased expressions (e.g. rawAs<number, 'avg_total'>) flow through to the query output and expression helpers know whether they produce booleans, numbers, etc.

  • refactor the query builder around a single state object: joins now widen the visible-table set (including aliases), predicates/order/group/having all read from that state, and the new selectConst() helper locks in literal column inference for downstream clauses. Runtime/type tests and docs were updated to cover alias-aware joins, HAVING-on-alias flows, and withCTE pipelines.

    Note: Because joins now register tables before the select clause is evaluated, builder chains that previously called .select() before .join() may surface new type errors. Reorder joins ahead of select clauses to resolve the stricter checking without a runtime change.

1.3.0Minor

Features

  • add predicate-builder callbacks (with ClickHouse function + logical helpers) to where/orWhere, enabling predicates like hasAny(tags, ['foo','bar']) without raw SQL; columns/arrays are inferred automatically and expr.raw() provides an escape hatch for edge cases

Get early access to hypequery cloud.

Cloud hosts the same model you run in your backend, with a managed API and MCP server, versions, and rollback. We're letting teams in a few at a time. Join the waitlist and we'll reach out when your project can go live.

How do you want to build your analytics layer? (Optional)
What features do you most need? (Optional)

Pick as many as you like.

Only your email is required. All other questions are optional.