Skip to content

Releases: ClickHouse/clickhouse-js

1.20.0

03 Jun 21:43
97f135e

Choose a tag to compare

Bug Fixes

  • (Node.js only) Fixed a race condition in ResultSet.json() and ResultSet.stream() on JSONEachRow (and other streamable) result sets where calling json() on a fast/small response could throw Stream has been already consumed if the underlying stream ended between internal readableEnded checks. The consumption guard has been hardened: the stream is now shielded through a single consume() path that marks the result set as consumed in the appropriate branches, after format validation, so a successful json() call no longer races against the stream finishing. (#603) kudos to @lord007tn and @Onyx2406

1.19.0

02 Jun 20:30
ee91a12

Choose a tag to compare

Improvements

  • Re-exported the ResponseHeaders type from @clickhouse/client and @clickhouse/client-web. Previously this type was only available from @clickhouse/client-common; it is now part of the public re-export surface of both flavored packages, alongside the other commonly used types. This is part of an ongoing effort to make @clickhouse/client-common an internal-only package so downstream consumers can depend solely on @clickhouse/client or @clickhouse/client-web. (#758)

Bug Fixes

  • Enum type parsing now correctly unescapes backslash escape sequences in enum names. Previously, parseEnumType returned enum names with raw escape sequences (e.g., f\' instead of f'). Now it properly decodes escape sequences including \' (single quote), \\ (backslash), \n (newline), \t (tab), and \r (carriage return). This matches the behavior of ClickHouse string literals and ensures consistency with how the client encodes strings when sending data to the server. If you were relying on the previous incorrect behavior where backslash escape sequences were preserved in enum names, you will need to update your code to handle properly unescaped values.

Example:

// Before (incorrect):
parseEnumType({
  columnType: "Enum8('f\\'' = 1)",
  sourceType: "Enum8('f\\'' = 1)",
})
// returned: { values: { 1: "f\\'" } }  // with backslash

// After (correct):
parseEnumType({
  columnType: "Enum8('f\\'' = 1)",
  sourceType: "Enum8('f\\'' = 1)",
})
// returns: { values: { 1: "f'" } }     // unescaped

1.18.5

13 May 22:29
24bbb66

Choose a tag to compare

Improvements

  • (Node.js only) Added max_response_headers_size client option that forwards the maxHeaderSize option to the underlying http(s).request call. This raises the per-request limit on the total size of HTTP response headers received from the server (Node.js default is ~16 KB). It is most useful when running long-running queries with send_progress_in_http_headers enabled — the X-ClickHouse-Progress headers accumulate over the lifetime of the request and can exceed the default limit, causing the request to fail with HPE_HEADER_OVERFLOW. Setting this option avoids the need to use the global --max-http-header-size Node.js CLI flag or the NODE_OPTIONS environment variable. Has no effect for the Web client (which uses fetch) and no effect when a custom http_agent is configured with a request implementation that does not honor the option.
const client = createClient({
  request_timeout: 400_000,
  max_response_headers_size: 1024 * 1024, // accept up to 1 MiB of response headers
  clickhouse_settings: {
    send_progress_in_http_headers: 1,
    http_headers_progress_interval_ms: '110000',
  },
})
  • The @clickhouse/client npm package now ships embedded AI-agent skills, clickhouse-js-node-coding and clickhouse-js-node-troubleshooting, under node_modules/@clickhouse/client/skills/. These skills are also declared in the agents.skills field of the package manifest for discovery tools that scan node_modules. This allows agentic coding tools to load focused, Node-client-specific coding and troubleshooting guidance without any additional setup. (#682)

1.18.4

13 May 22:28
010172d

Choose a tag to compare

A release-infrastructure-only version bump (no user-facing changes). See 1.18.5 for the next release with user-facing improvements.

1.18.3

13 May 22:27
d99a4e2

Choose a tag to compare

Improvements

  • Added keep_alive.eagerly_destroy_stale_sockets option (Node.js only, default: false). When enabled, sockets that have been idle for longer than idle_socket_ttl are destroyed immediately before each request, rather than waiting for the idle timeout to fire. This helps reclaim stale sockets during event loop delays, where the timeout callback may not run on time.
const client = createClient({
  keep_alive: {
    enabled: true,
    idle_socket_ttl: 2500,
    eagerly_destroy_stale_sockets: true,
  },
})
  • Added auto-detection and warning when request_timeout is high (> 60 seconds) but progress headers are not configured. Long-running queries may fail with socket hang-up errors if they exceed the load balancer idle timeout. The client now warns users to enable send_progress_in_http_headers and http_headers_progress_interval_ms settings to prevent such issues.
// This will now trigger a warning
const client = createClient({
  request_timeout: 120_000, // 120 seconds
  // send_progress_in_http_headers is not configured
})

// ✓ Properly configured to avoid load balancer timeouts
const client = createClient({
  request_timeout: 400_000,
  clickhouse_settings: {
    send_progress_in_http_headers: 1,
    http_headers_progress_interval_ms: '110000', // ~10s below LB timeout
  },
})

1.18.2

09 Mar 13:59
2fcb6a6

Choose a tag to compare

Improvements

  • Added a helping WARN level log message with a suggestion to check the keep_alive configuration if the client receives an ECONNRESET error from the server, which can happen when the server closes idle connections after a certain timeout, and the client tries to reuse such a connection from the pool. This can be especially helpful for new users who might not be aware of this aspect of HTTP connection management. The log message is only emitted if the keep_alive option is enabled in the client configuration, and it includes the server's keep-alive timeout value (if available) to assist with troubleshooting. (#597)

How to reproduce the issue that triggers the log message:

const client = createClient({
  // ...
  keep_alive: {
    enabled: true,
    // ❌ DON'T SET THIS VALUE SO HIGH IN PRODUCTION
    idle_socket_ttl: 1_000_000,
  },
  log: {
    level: ClickHouseLogLevel.WARN, // to see the warning logs
  },
})

for (let i = 0; i < 1000; i++) {
  await client.ping({
    // To use a regular query instead of the /ping endpoint
    // which might be configured differently on the server side
    // and have different timeout settings.
    select: true,
  })

  // Wait long enough to let the server close the idle connection,
  // but not too long to let the client remove it from the pool,
  // in other words try to hit the scenario when the race condition
  // happens between the server closing the connection and the client
  // trying to reuse it.
  await sleep(SERVER_KEEP_ALIVE_TIMEOUT_MS - 100)
}

Example log message:

{
  "message": "Ping: idle socket TTL is greater than server keep-alive timeout, try setting idle socket TTL to a value lower than the server keep-alive timeout to prevent unexpected connection resets, see https://c.house/js_keep_alive_econnreset for more details.",
  "args": {
    "operation": "Ping",
    "connection_id": "8dc1c9bd-7895-49b1-8a95-276470151c65",
    "query_id": "beee95af-2e83-4dcb-8e1e-045bd61f4985",
    "request_id": "8dc1c9bd-7895-49b1-8a95-276470151c65:2",
    "socket_id": "8dc1c9bd-7895-49b1-8a95-276470151c65:1",
    "server_keep_alive_timeout_ms": 10000,
    "idle_socket_ttl": 15000
  },
  "module": "HTTP Adapter"
}

1.18.1

02 Mar 16:13
cbdd7bf

Choose a tag to compare

Improvements

  • Setting log.level default value to ClickHouseLogLevel.WARN instead of ClickHouseLogLevel.OFF to provide better visibility into potential issues without overwhelming users with too much information by default.
const client = createClient({
  // ...
  log: {
    level: ClickHouseLogLevel.WARN, // default is now ClickHouseLogLevel.WARN instead of ClickHouseLogLevel.OFF
  },
})
  • Logging is now lazy, which means that the log messages will only be constructed if the log level is appropriate for the message. This can improve performance in cases where constructing the log message is expensive, and the log level is set to ignore such messages. See ClickHouseLogLevel enum for the complete list of log levels. (#520)
const client = createClient({
  // ...
  log: {
    level: ClickHouseLogLevel.TRACE, // to log everything available down to the network level events
  },
})
  • Enhanced the logging of the HTTP request / socket lifecycle with additional trace messages and context such as Connection ID (UUID) and Request ID and Socket ID that embed the connection ID for ease of tracing the logs of a particular request across the connection lifecycle. To enable such logs, set the log.level config option to ClickHouseLogLevel.TRACE. (#567)
[2026-02-25T09:19:13.511Z][TRACE][@clickhouse/client][Connection] Insert: received 'close' event, 'free' listener removed
Arguments: {
  operation: 'Insert',
  connection_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c',
  query_id: '9dfda627-39a2-41a6-9fc9-8f8716574826',
  request_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c:3',
  socket_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c:2',
  event: 'close'
}
[2026-02-25T09:19:13.502Z][TRACE][@clickhouse/client][Connection] Query: reusing socket
Arguments: {
  operation: 'Query',
  connection_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c',
  query_id: 'ad0127e8-b1c7-4ed6-9681-c0162f7a0ea9',
  request_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c:4',
  socket_id: 'da3c9796-5dc5-46ef-83b0-ed1f4422094c:2',
  usage_count: 1
}
  • A step towards structured logging: the client now passes rich context to the logger args parameter (e.g. connection_id, query_id, request_id, socket_id). (#576)

Deprecated API

  • The drainStream utility function is now deprecated, as the client will handle draining the stream internally when needed. Use client.command() instead, which will handle draining the stream internally when needed. (#578)

  • The sleep utility function is now deprecated, as it is not intended to be used outside of the client implementation. Use setTimeout directly or a more full-featured utility library if you need additional features like cancellation or timers management. (#578)

1.17.0

06 Feb 16:41

Choose a tag to compare

New features

  • Added http_status_code to query, insert, and exec commands (#525, [Kinzeng])
  • Fixed ignore_error_response not getting passed when using command (#536, [Kinzeng])

1.16.0

07 Jan 11:44
78960f6

Choose a tag to compare

New features

async function main() {
  await using client = await client.query();

  // some code that can throw
  // but thanks to `using` the client will still get closed

  // client is also automatically closed here by calling [Symbol.disaposeAsync]
}

Without the new using keyword it is required to wrap the code that might leak expensive resources like sockets and big buffers in try / finally

async function main() {
  let client
  try {
    client = await createClient();
    // some code that can throw
  } finally {
    if (client) {
      await client.close()
    }
  }
}

1.15.0

07 Jan 11:25
3572448

Choose a tag to compare

New features