跳转到主要内容
用于连接 ClickHouse 的官方 JavaScript 客户端。 该客户端使用 TypeScript 编写,并为客户端公开 API 提供类型定义。 它零依赖,针对极致性能进行了优化,并已在多种 ClickHouse 版本和配置下通过测试 (本地部署单节点、本地部署集群以及 ClickHouse Cloud) 。 针对不同环境,提供了两个不同版本的客户端:
  • @clickhouse/client - 仅限 Node.js
  • @clickhouse/client-web - 浏览器 (Chrome/Firefox) 、Cloudflare Workers
使用 TypeScript 时,请确保版本至少为 4.5,该版本支持内联 import 和 export 语法 客户端源代码可在 ClickHouse-JS GitHub 仓库 中获取。
AI 智能体技能JavaScript 客户端随附 AI 智能体技能,可帮助编码智能体使用该客户端。使用以下命令安装:
npm skills add ClickHouse/clickhouse-js

环境要求 (node.js)

环境中必须安装可用的 Node.js,才能运行客户端。 该客户端兼容所有仍在维护中的 Node.js 发行版 一旦某个 Node.js 版本接近生命周期终止 (End-Of-Life) ,客户端就会停止支持该版本,因为它会被视为过期且存在安全风险。 当前 Node.js 版本的支持情况如下:
Node.js 版本是否支持?
24.x
22.x
20.x
18.x尽力支持

环境要求 (web)

该客户端的 web 版本已在最新版本的 Chrome/Firefox 浏览器中通过官方测试,并且可作为依赖项用于 React/Vue/Angular 应用或 Cloudflare Workers 等环境。

安装

要安装最新版的稳定版 Node.js 客户端,请运行:
npm i @clickhouse/client
Web 版安装:
npm i @clickhouse/client-web

与 ClickHouse 的兼容性

客户端版本ClickHouse
1.12.024.8+
客户端也很可能可用于较早版本;不过,这类支持仅属尽力而为,不作保证。如果你的 ClickHouse 版本早于 23.3,请参阅 ClickHouse 安全策略 并考虑升级。

示例

我们力求通过客户端代码仓库中的 examples 涵盖客户端使用的各种场景。 你可以在 examples README 中查看概览。 如果示例或下文档中的某些内容不够清楚,或有缺失,欢迎联系我们

客户端 API

除非另有明确说明,否则大多数示例同时适用于 Node.js 版和 Web 版客户端。

创建客户端实例

你可以根据需要使用 createClient 工厂函数创建任意数量的客户端实例:
import { createClient } from '@clickhouse/client' // 或 '@clickhouse/client-web'

const client = createClient({
  /* 配置 */
})
如果您的环境不支持 ESM 模块,也可以改用 CJS 语法:
const { createClient } = require('@clickhouse/client');

const client = createClient({
  /* 配置 */
})
客户端实例可以在实例化时进行预先配置

配置

创建客户端实例时,可以调整以下连接设置:
SettingDescriptionDefault ValueSee Also
url?: stringClickHouse 实例的 URL。http://localhost:8123URL 配置文档
pathname?: string可选路径名,客户端解析 ClickHouse URL 后会将其附加到 URL 之后。''带路径名的代理文档
request_timeout?: number请求超时时间 (以毫秒为单位) 。30_000-
compression?: { **response**?: boolean; **request**?: boolean }启用压缩。-压缩文档
username?: string发出请求时使用的用户名。default-
password?: string用户密码。''-
application?: string使用 Node.js 客户端的应用程序名称。clickhouse-js-
database?: string要使用的数据库名称。default-
clickhouse_settings?: ClickHouseSettings应用于所有请求的 ClickHouse 设置。{}-
log?: { **LoggerClass**?: Logger, **level**?: ClickHouseLogLevel }客户端内部日志配置。-日志文档
session_id?: string可选的 ClickHouse Session ID,会随每个请求一并发送。--
keep_alive?: { **enabled**?: boolean }在 Node.js 和 Web 版本中默认启用。--
http_headers?: Record<string, string>发往 ClickHouse 的请求中附加的额外 HTTP 请求头。-带身份验证的反向代理文档
roles?: stringstring[]要附加到出站请求中的 ClickHouse 角色名称。-在 HTTP interface 中使用角色

Node.js 专用配置参数

设置描述默认值另请参见
max_open_connections?: number每个主机允许的最大连接套接字数。10-
tls?: { **ca_cert**: Buffer, **cert**?: Buffer, **key**?: Buffer }配置 TLS 证书。-TLS 文档
keep_alive?: { **enabled**?: boolean, **idle_socket_ttl**?: number }--Keep-Alive 文档
http_agent?: http.Agenthttps.Agent
为客户端指定自定义 HTTP Agent。-HTTP agent 文档
set_basic_auth_header?: boolean
将基本身份验证凭据设置到 Authorization 请求头中。trueHTTP agent 文档中此设置的用法

URL 配置

URL 配置会始终覆盖硬编码的值,并在这种情况下记录一条警告日志。
大多数客户端实例参数都可以通过 URL 进行配置。URL 格式为 http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]。在绝大多数情况下,某个参数的名称都对应于它在 config 选项 interface 中的路径,只有少数例外。支持以下参数:
ParameterType
pathname任意字符串。
application_id任意字符串。
session_id任意字符串。
request_timeout非负数。
max_open_connections大于零的非负数。
compression_request布尔值。见下文 (1)
compression_response布尔值。
log_level允许的值:OFFTRACEDEBUGINFOWARNERROR
keep_alive_enabled布尔值。
clickhouse_setting_* or ch_*见下文 (2)
http_header_*见下文 (3)
(Node.js only) keep_alive_idle_socket_ttl非负数。
  • (1) 对于布尔值,有效值为 true/1false/0
  • (2) 任何带有 clickhouse_setting_ch_ 前缀的参数,都会移除该前缀,并将其余部分添加到客户端的 clickhouse_settings 中。例如,?ch_async_insert=1&ch_wait_for_async_insert=1 与以下配置相同:
createClient({
  clickhouse_settings: {
    async_insert: 1,
    wait_for_async_insert: 1,
  },
})
注意:clickhouse_settings 的布尔值应在 URL 中以 1/0 形式传递。
  • (3) 与 (2) 类似,但适用于 http_header 配置。例如,?http_header_x-clickhouse-auth=foobar 等同于:
createClient({
  http_headers: {
    'x-clickhouse-auth': 'foobar',
  },
})

建立连接

获取连接信息

要通过 HTTP(S) 连接到 ClickHouse,你需要以下信息:
Parameter(s)Description
HOST and PORT通常,使用 TLS 时端口为 8443;不使用 TLS 时端口为 8123。
DATABASE NAME默认情况下,存在一个名为 default 的数据库。请使用你要连接的数据库名称。
USERNAME and PASSWORD默认情况下,用户名为 default。请根据你的使用场景使用相应的用户名。
你的 ClickHouse Cloud 服务的连接信息可在 ClickHouse Cloud 控制台中查看。 选择一个服务,然后点击 Connect 选择 HTTPS。连接信息会显示在示例 curl 命令中。 如果你使用的是自管理 ClickHouse,则连接信息由你的 ClickHouse 管理员配置。

连接概览

该客户端通过 HTTP 或 HTTPS 协议建立连接。对 RowBinary 的支持正在推进中,参见相关 issue 以下示例演示了如何连接到 ClickHouse Cloud。这里假设 url (包括 协议和端口) 以及 password 值通过环境变量指定,并使用 default 用户。 **示例:**使用环境变量进行配置,创建 Node.js 客户端实例。
import { createClient } from '@clickhouse/client'

const client = createClient({
  url: process.env.CLICKHOUSE_HOST ?? 'http://localhost:8123',
  username: process.env.CLICKHOUSE_USER ?? 'default',
  password: process.env.CLICKHOUSE_PASSWORD ?? '',
})
客户端代码仓库包含多个使用环境变量的示例,例如在 ClickHouse Cloud 中创建表使用异步插入以及许多其他示例。

连接池 (仅限 Node.js)

为避免每次请求都建立连接带来的开销,客户端会创建一个到 ClickHouse 的连接池以供复用,并利用 Keep-Alive 机制。默认情况下,Keep-Alive 已启用,连接池大小设为 10,但你可以通过 max_open_connections 配置选项 进行更改。 除非用户设置 max_open_connections: 1,否则无法保证连接池中的同一个连接会用于后续查询。这种需求很少见,但在用户使用临时表时可能是必需的。 另请参见:Keep-Alive 配置

查询 ID

每个发送查询或语句的方法 (commandexecinsertselect) 都会在结果中返回 query_id。这个唯一标识符由客户端为每个查询分配,可用于从 system.query_log 中获取数据, 前提是已在服务器配置中启用该日志;也可用于取消长时间运行的查询 (参见该示例) 。如有需要,用户可以在 command/query/exec/insert 方法的参数中覆盖 query_id
如果你要覆盖 query_id 参数,需要确保它在每次调用中都唯一。随机 UUID 是个不错的选择。

所有客户端方法的基础参数

以下几个参数适用于所有客户端方法 (query/command/insert/exec) 。
interface BaseQueryParams {
  // 可在查询级别应用的 ClickHouse 设置。
  clickhouse_settings?: ClickHouseSettings
  // 用于查询绑定的参数。
  query_params?: Record<string, unknown>
  // 用于取消正在进行的查询的 AbortSignal 实例。
  abort_signal?: AbortSignal
  // query_id 覆盖值;若未指定,将自动生成随机标识符。
  query_id?: string
  // session_id 覆盖值;若未指定,将从客户端配置中获取会话 ID。
  session_id?: string
  // 凭据覆盖值;若未指定,将使用客户端的凭据。
  auth?: { username: string, password: string }
  // 用于本次查询的特定角色列表。覆盖客户端配置中设置的角色。
  role?: string | Array<string>
}

查询方法

此方法适用于大多数会返回响应的语句,例如 SELECT,也可用于发送 CREATE TABLE 之类的 DDLs,因此应使用 await 等待其完成。返回的结果集通常应由应用程序进行处理。
对于数据插入,有专门的 insert 方法;对于 DDLs,则有 command 方法。
interface QueryParams extends BaseQueryParams {
  // 要执行的查询,可能会返回数据。
  query: string
  // 返回结果集的格式。默认值:JSON。
  format?: DataFormat
}

interface ClickHouseClient {
  query(params: QueryParams): Promise<ResultSet>
}
另请参见:所有客户端方法的基础参数
不要在 query 中指定 FORMAT 子句,请改用 format 参数。

结果集与行抽象

ResultSet 提供了多种便捷方法,方便你在应用程序中处理数据。 Node.js 中的 ResultSet 实现底层使用 Stream.Readable,而 Web 版本使用 Web API ReadableStream 你可以调用 ResultSettextjson 方法来读取 ResultSet,并将查询返回的全部行一次性加载到内存中。 你应尽快开始读取 ResultSet,因为它会一直保持响应流处于打开状态,从而占用底层连接。客户端不会缓冲传入的数据,以避免应用程序出现过高的内存使用。 或者,如果数据量太大,无法一次性放入内存,你可以调用 stream 方法,以流式方式处理数据。这样,响应中的每个 chunk 都会被转换为一个相对较小的行数组 (数组大小取决于客户端从 server 接收到的具体 chunk 大小——该大小可能会变化——以及单行的大小) ,然后逐个 chunk 进行处理。 请参阅支持的数据格式列表,以确定哪种格式最适合你的流式处理场景。例如,如果你想流式处理 JSON 对象,可以选择 JSONEachRow,这样每一行都会被解析为一个 JS 对象;或者也可以选择更紧凑的 JSONCompactColumns 格式,这样每一行都会成为一个紧凑的值数组。另请参阅:文件流式传输
如果 ResultSet 或其 stream 未被完全读取,则会在空闲达到 request_timeout 后被销毁。
interface BaseResultSet<Stream> {
  // 参见上方"Query ID"部分
  query_id: string

  // 消费整个 stream 并以字符串形式获取内容
  // 可与任意 DataFormat 配合使用
  // 只应调用一次
  text(): Promise<string>

  // 消费整个 stream 并将内容解析为 JS 对象
  // 仅可与 JSON formats 配合使用
  // 只应调用一次
  json<T>(): Promise<T>

  // 返回可读 stream,用于以流式方式处理响应
  // 每次迭代 stream 时,将以所选 DataFormat 返回一个 Row[] 数组
  // 只应调用一次
  stream(): Stream
}

interface Row {
  // 以纯字符串形式获取行的内容
  text: string

  // 将行的内容解析为 JS 对象
  json<T>(): T
}
示例: (Node.js/Web) 一个查询示例:结果数据集采用 JSONEachRow 格式,读取整个 stream,并将其内容解析为 JS 对象。 源代码
const resultSet = await client.query({
  query: 'SELECT * FROM my_table',
  format: 'JSONEachRow',
})
const dataset = await resultSet.json() // 或使用 `row.text`,避免解析 JSON
示例: (仅限 Node.js) 使用经典的 on('data') 方法,以 JSONEachRow 格式流式处理查询结果。这种方式可与 for await const 语法互换使用。源代码
const rows = await client.query({
  query: 'SELECT number FROM system.numbers_mt LIMIT 5',
  format: 'JSONEachRow', // 或 JSONCompactEachRow、JSONStringsEachRow 等
})
const stream = rows.stream()
stream.on('data', (rows: Row[]) => {
  rows.forEach((row: Row) => {
    console.log(row.json()) // 或使用 `row.text` 以避免解析 JSON
  })
})
await new Promise((resolve, reject) => {
  stream.on('end', () => {
    console.log('Completed!')
    resolve(0)
  })
  stream.on('error', reject)
})
示例: (仅限 Node.js) 使用经典的 on('data') 方式,以 CSV 格式流式处理查询结果。这种方式可与 for await const 语法互换使用。 源代码
const resultSet = await client.query({
  query: 'SELECT number FROM system.numbers_mt LIMIT 5',
  format: 'CSV', // 或 TabSeparated、CustomSeparated 等
})
const stream = resultSet.stream()
stream.on('data', (rows: Row[]) => {
  rows.forEach((row: Row) => {
    console.log(row.text)
  })
})
await new Promise((resolve, reject) => {
  stream.on('end', () => {
    console.log('Completed!')
    resolve(0)
  })
  stream.on('error', reject)
})
示例: (仅限 Node.js) 使用 for await const 语法,以 JS 对象形式消费 JSONEachRow 格式的流式查询结果。这种方式可与经典的 on('data') 用法互换。 源代码.
const resultSet = await client.query({
  query: 'SELECT number FROM system.numbers LIMIT 10',
  format: 'JSONEachRow', // 或 JSONCompactEachRow、JSONStringsEachRow 等
})
for await (const rows of resultSet.stream()) {
  rows.forEach(row => {
    console.log(row.json())
  })
}
for await const 语法比 on('data') 方式需要的代码更少,但可能会对性能产生负面影响。 详情请参见 Node.js 仓库中的这个 issue
示例: (仅 Web) 遍历对象的 ReadableStream
const resultSet = await client.query({
  query: 'SELECT * FROM system.numbers LIMIT 10',
  format: 'JSONEachRow'
})

const reader = resultSet.stream().getReader()
while (true) {
  const { done, value: rows } = await reader.read()
  if (done) { break }
  rows.forEach(row => {
    console.log(row.json())
  })
}

insert 方法

这是用于插入数据的主要方法。
export interface InsertResult {
  query_id: string
  executed: boolean
}

interface ClickHouseClient {
  insert(params: InsertParams): Promise<InsertResult>
}
返回类型被尽量保持精简,因为我们预计服务器不会返回任何数据,并会立即耗尽响应流。 如果向 insert 方法提供的是空数组,则不会将 insert 语句发送到服务器;相反,该方法会立即返回 { query_id: '...', executed: false }。在这种情况下,如果未在方法参数中提供 query_id,则结果中的它将是空字符串,因为返回由客户端生成的随机 UUID 可能会造成混淆,毕竟带有该 query_id 的查询并不存在于 system.query_log 表中。 如果 insert 语句已发送到服务器,则 executed 标志将为 true

Insert 方法与 Node.js 中的流式处理

它既可以与 Stream.Readable 配合使用,也可以使用普通的 Array<T>,具体取决于为 insert 方法指定的数据格式。另请参阅本节中关于文件流式传输的内容。 通常应使用 await 等待 Insert 方法完成;不过,也可以先指定一个输入流,等到流结束后再等待 insert 操作 (这也会使 insert promise 完成) 。这在事件监听器等类似场景中可能很有用,但客户端侧的错误处理并不简单,而且会有很多边界情况。作为替代方案,建议使用异步插入,如此示例所示。
如果你有难以用此方法表达的自定义 INSERT 语句,可以考虑使用 command 方法你可以在 INSERT INTO … VALUESINSERT INTO … SELECT 示例中查看其用法。
interface InsertParams<T> extends BaseQueryParams {
  // 要插入数据的表名
  table: string
  // 要插入的数据集。
  values: ReadonlyArray<T> | Stream.Readable
  // 要插入的数据集格式。
  format?: DataFormat
  // 用于指定数据将插入哪些列。
  // - 数组形式如 `['a', 'b']` 将生成:`INSERT INTO table (a, b) FORMAT DataFormat`
  // - 对象形式如 `{ except: ['a', 'b'] }` 将生成:`INSERT INTO table (* EXCEPT (a, b)) FORMAT DataFormat`
  // 默认情况下,数据将插入表的所有列,
  // 生成的语句为:`INSERT INTO table FORMAT DataFormat`。
  columns?: NonEmptyArray<string> | { except: NonEmptyArray<string> }
}
另请参见:所有客户端方法的基础参数
使用 abort_signal 取消请求,并不能保证未发生数据插入,因为 server 可能在取消前已经接收了部分流式传输的数据。
示例: (Node.js/Web) 插入值数组。 源代码
await client.insert({
  table: 'my_table',
  // 结构需与目标格式匹配,本例中为 JSONEachRow
  values: [
    { id: 42, name: 'foo' },
    { id: 42, name: 'bar' },
  ],
  format: 'JSONEachRow',
})
示例: (仅限 Node.js) 插入来自 CSV 文件的流。 源代码。另请参见:文件流式传输
await client.insert({
  table: 'my_table',
  values: fs.createReadStream('./path/to/a/file.csv'),
  format: 'CSV',
})
示例:在 insert 语句中排除某些列。 例如,给定如下表定义:
CREATE OR REPLACE TABLE mytable
(id UInt32, message String)
ENGINE MergeTree()
ORDER BY (id)
仅插入指定列:
// 生成的语句:INSERT INTO mytable (message) FORMAT JSONEachRow
await client.insert({
  table: 'mytable',
  values: [{ message: 'foo' }],
  format: 'JSONEachRow',
  // 此行的 `id` 列值将为零(UInt32 的默认值)
  columns: ['message'],
})
排除某些列:
// 生成的语句:INSERT INTO mytable (* EXCEPT (message)) FORMAT JSONEachRow
await client.insert({
  table: tableName,
  values: [{ id: 144 }],
  format: 'JSONEachRow',
  // 此行的 `message` 列值将为空字符串
  columns: {
    except: ['message'],
  },
})
更多详情请参阅源代码 示例:插入到与客户端实例所指定数据库不同的数据库中。源代码
await client.insert({
  table: 'mydb.mytable', // 包含数据库名的完全限定名称
  values: [{ id: 42, message: 'foo' }],
  format: 'JSONEachRow',
})

Web 版本限制

目前,@clickhouse/client-web 中的插入操作仅支持 Array<T>JSON* 格式。 由于浏览器兼容性不佳,Web 版本目前尚不支持通过流进行插入。 因此,Web 版本的 InsertParams 接口与 Node.js 版本略有不同, 因为 values 仅限为 ReadonlyArray<T> 类型:
interface InsertParams<T> extends BaseQueryParams {
  // 要插入数据的表名
  table: string
  // 要插入的数据集。
  values: ReadonlyArray<T>
  // 要插入的数据集的格式。
  format?: DataFormat
  // 用于指定数据将插入哪些列。
  // - 数组形式如 `['a', 'b']` 将生成:`INSERT INTO table (a, b) FORMAT DataFormat`
  // - 对象形式如 `{ except: ['a', 'b'] }` 将生成:`INSERT INTO table (* EXCEPT (a, b)) FORMAT DataFormat`
  // 默认情况下,数据将插入表的所有列,
  // 生成的语句为:`INSERT INTO table FORMAT DataFormat`。
  columns?: NonEmptyArray<string> | { except: NonEmptyArray<string> }
}
此项内容今后可能会有所变动。另请参见:所有客户端方法的基础参数

Command 方法

它可用于执行没有任何输出的语句、FORMAT 子句不适用的情况,或者你根本不关心响应内容的情况。例如,这类语句可以是 CREATE TABLEALTER TABLE 应等待其完成。 响应流会立即销毁,这意味着底层套接字会被释放。
interface CommandParams extends BaseQueryParams {
  // 要执行的语句。
  query: string
}

interface CommandResult {
  query_id: string
}

interface ClickHouseClient {
  command(params: CommandParams): Promise<CommandResult>
}
另请参见:所有客户端方法的基础参数 示例: (Node.js/Web) 在 ClickHouse Cloud 中创建表。 源代码
await client.command({
  query: `
    CREATE TABLE IF NOT EXISTS my_cloud_table
    (id UInt64, name String)
    ORDER BY (id)
  `,
  // 建议在集群环境中使用,以避免响应代码返回后才发生查询处理错误,
  // 而 HTTP 请求头 已经发送给客户端的情况。
  // 参见 https://clickhouse.com/docs/interfaces/http/#response-buffering
  clickhouse_settings: {
    wait_end_of_query: 1,
  },
})
示例: (Node.js/Web) 在自托管的 ClickHouse 实例中创建表。 源代码.
await client.command({
  query: `
    CREATE TABLE IF NOT EXISTS my_table
    (id UInt64, name String)
    ENGINE MergeTree()
    ORDER BY (id)
  `,
})
示例: (Node.js/Web) INSERT FROM SELECT
await client.command({
  query: `INSERT INTO my_table SELECT '42'`,
})
使用 abort_signal 取消请求,并不能保证该语句未在服务器端执行。

Exec 方法

如果你有一个无法通过 query/insert 处理的自定义查询, 并且需要获取返回结果,可以使用 exec 作为 command 的替代方案。 exec 会返回一个可读流,必须由应用程序端消费或销毁。
interface ExecParams extends BaseQueryParams {
  // 要执行的语句。
  query: string
}

interface ClickHouseClient {
  exec(params: ExecParams): Promise<QueryResult>
}
另请参阅:所有客户端方法的基础参数 Node.js 与 Web 版本中的 stream 返回类型不同。 Node.js:
export interface QueryResult {
  stream: Stream.Readable
  query_id: string
}
Web:
export interface QueryResult {
  stream: ReadableStream
  query_id: string
}

Ping

用于检查连接状态的 ping 方法在服务器可达时会返回 true 如果服务器不可达,结果中也会包含底层错误信息。
type PingResult =
  | { success: true }
  | { success: false; error: Error }

/** 健康检查请求的参数 - 使用内置的 `/ping` 端点。
 *  这是 Node.js 版本的默认行为。*/
export type PingParamsWithEndpoint = {
  select: false
  /** AbortSignal 实例,用于取消正在进行的请求。*/
  abort_signal?: AbortSignal
  /** 附加到此请求的额外 HTTP 请求头。*/
  http_headers?: Record<string, string>
}
/** 健康检查请求的参数 - 使用 SELECT 查询。
 *  这是 Web 版本的默认行为,因为 `/ping` 端点不支持 CORS。
 *  大多数标准 `query` 方法参数(如 `query_id`、`abort_signal`、`http_headers` 等)均可使用,
 *  但 `query_params` 除外,该参数在此方法中无实际意义。*/
export type PingParamsWithSelectQuery = { select: true } & Omit<
  BaseQueryParams,
  'query_params'
>
export type PingParams = PingParamsWithEndpoint | PingParamsWithSelectQuery

interface ClickHouseClient {
  ping(params?: PingParams): Promise<PingResult>
}
Ping 可作为应用启动时检查服务器是否可用的一个实用工具,尤其是在 ClickHouse Cloud 中,实例可能处于空闲状态,并会在收到 ping 后被唤醒:在这种情况下,你可能需要在每次尝试之间加入延迟,并重试几次。 请注意,默认情况下,Node.js 版本使用 /ping 端点,而 Web 版本则使用简单的 SELECT 1 查询来达到类似效果,因为 /ping 端点不支持 CORS。 示例: (Node.js/Web) 向 ClickHouse 服务器实例发送一个简单的 ping。注意:对于 Web 版本,捕获到的错误会有所不同。 源代码
const result = await client.ping();
if (!result.success) {
  // 处理 result.error
}
**示例:**如果你希望在调用 ping 方法时也一并校验凭据,或指定额外参数 (如 query_id) ,可以按如下方式使用:
const result = await client.ping({ select: true, /* query_id、abort_signal、http_headers 或其他任意查询参数 */ });
ping 方法支持大多数标准 query 方法的参数——请参阅 PingParamsWithSelectQuery 类型定义。

关闭 (仅限 Node.js)

关闭所有已打开的连接并释放资源。在 web 版本中,此操作为空操作。
await client.close()

文件流式传输 (仅限 Node.js)

客户端代码仓库中提供了若干使用常见数据格式 (NDJSON、CSV、Parquet) 的文件流式传输示例。 将其他格式流式传输到文件中的方式应与 Parquet 类似, 唯一的区别在于 query 调用中使用的格式 (JSONEachRowCSV 等) 以及输出文件名。

支持的数据格式

客户端可处理 JSON 或文本格式的数据。 如果将 format 指定为 JSON 家族中的某种格式 (JSONEachRowJSONCompactEachRow 等) ,客户端会在传输过程中对数据进行序列化和反序列化。 以“原始”文本格式 (CSVTabSeparatedCustomSeparated 家族) 提供的数据会直接发送,不会进行额外转换。
JSON 作为一种通用格式,可能会与 ClickHouse JSON format 混淆。客户端支持以 JSONEachRow 等格式流式传输 JSON objects (其他适合流式处理的格式请参见表格概览;另请参见客户端代码仓库中的 select_streaming_ 示例) 。只是像 ClickHouse JSON 这样的格式以及其他少数几种格式,在响应中会表示为单个对象,因此客户端无法对其进行流式处理。
格式输入 (数组)输入 (对象)输入/输出 (流)输出 (JSON)输出 (文本)
JSON✔️✔️✔️
JSONCompact✔️✔️✔️
JSONObjectEachRow✔️✔️✔️
JSONColumnsWithMetadata✔️✔️✔️
JSONStrings❌️✔️✔️
JSONCompactStrings✔️✔️
JSONEachRow✔️✔️✔️✔️
JSONEachRowWithProgress❌️✔️ ❗- 见下文✔️✔️
JSONStringsEachRow✔️✔️✔️✔️
JSONCompactEachRow✔️✔️✔️✔️
JSONCompactStringsEachRow✔️✔️✔️✔️
JSONCompactEachRowWithNames✔️✔️✔️✔️
JSONCompactEachRowWithNamesAndTypes✔️✔️✔️✔️
JSONCompactStringsEachRowWithNames✔️✔️✔️✔️
JSONCompactStringsEachRowWithNamesAndTypes✔️✔️✔️✔️
CSV✔️✔️
CSVWithNames✔️✔️
CSVWithNamesAndTypes✔️✔️
TabSeparated✔️✔️
TabSeparatedRaw✔️✔️
TabSeparatedWithNames✔️✔️
TabSeparatedWithNamesAndTypes✔️✔️
CustomSeparated✔️✔️
CustomSeparatedWithNames✔️✔️
CustomSeparatedWithNamesAndTypes✔️✔️
Parquet✔️✔️❗- 见下文
对于 Parquet,select 的主要用途很可能是将结果流写入文件。请参见客户端代码仓库中的该示例 JSONEachRowWithProgress 是一种仅用于输出的格式,支持在流中报告进度。更多详情请参见此示例 ClickHouse 输入和输出格式的完整列表可在 此处查看。

支持的 ClickHouse 数据类型

对应的 JS 类型适用于所有 JSON* 格式,但不包括将所有内容都表示为字符串的格式 (例如 JSONStringEachRow)
类型状态JS 类型
UInt8/16/32✔️number
UInt64/128/256✔️ ❗- 见下文string
Int8/16/32✔️number
Int64/128/256✔️ ❗- 见下文string
Float32/64✔️number
Decimal✔️ ❗- 见下文number
Boolean✔️boolean
String✔️string
FixedString✔️string
UUID✔️string
Date32/64✔️string
DateTime32/64✔️ ❗- 见下文string
Enum✔️string
LowCardinality✔️string
Array(T)✔️T[]
(new) JSON✔️object
Variant(T1, T2…)✔️T (取决于具体变体)
Dynamic✔️T (取决于具体变体)
Nested✔️T[]
Tuple(T1, T2, …)✔️[T1, T2, …]
Tuple(n1 T1, n2 T2…)✔️{ n1: T1; n2: T2; …}
Nullable(T)✔️T 对应的 JS 类型或 null
IPv4✔️string
IPv6✔️string
Point✔️[ number, number ]
Ring✔️Array<Point>
Polygon✔️Array<Ring>
MultiPolygon✔️Array<Polygon>
Map(K, V)✔️Record<K, V>
Time/Time64✔️string
完整的受支持 ClickHouse 格式列表可在 此处查看。 另请参阅:

Date/Date32 类型注意事项

由于客户端在插入值时不会进行额外的类型转换,因此 Date/Date32 类型的列只能以字符串形式插入。 **示例:**插入一个 Date 类型的值。 源代码
await client.insert({
  table: 'my_table',
  values: [ { date: '2022-09-05' } ],
  format: 'JSONEachRow',
})
不过,如果你使用的是 DateTimeDateTime64 列,也可以同时使用字符串和 JS Date 对象。将 date_time_input_format 设为 best_effort 时,JS Date 对象可以原样传递给 insert。更多详情请参见这个示例

Decimal* 类型的注意事项

可以使用 JSON* 家族格式插入 Decimal 类型的值。假设定义了如下表:
CREATE TABLE my_table
(
  id     UInt32,
  dec32  Decimal(9, 2),
  dec64  Decimal(18, 3),
  dec128 Decimal(38, 10),
  dec256 Decimal(76, 20)
)
ENGINE MergeTree()
ORDER BY (id)
我们可以使用字符串表示形式插入值,且不会损失精度:
await client.insert({
  table: 'my_table',
  values: [{
    id: 1,
    dec32:  '1234567.89',
    dec64:  '123456789123456.789',
    dec128: '1234567891234567891234567891.1234567891',
    dec256: '12345678912345678912345678911234567891234567891234567891.12345678911234567891',
  }],
  format: 'JSONEachRow',
})
但是,在以 JSON* 格式查询数据时,ClickHouse 默认会将 Decimal 作为数值返回,这可能会导致精度丢失。为避免这种情况,你可以在查询中将 Decimal 转换为字符串:
await client.query({
  query: `
    SELECT toString(dec32)  AS decimal32,
           toString(dec64)  AS decimal64,
           toString(dec128) AS decimal128,
           toString(dec256) AS decimal256
    FROM my_table
  `,
  format: 'JSONEachRow',
})
更多详细信息,请参见此示例

整数类型:Int64、Int128、Int256、UInt64、UInt128、UInt256

虽然服务器可以将其作为数值接收,但为了避免整数溢出,在 JSON* 家族输出格式中,它会以字符串形式返回, 因为这些类型的最大值超过了 Number.MAX_SAFE_INTEGER 不过,这种行为可以通过 output_format_json_quote_64bit_integers 设置 进行修改。 **示例:**调整 64 位数值的 JSON 输出格式。
const resultSet = await client.query({
  query: 'SELECT * from system.numbers LIMIT 1',
  format: 'JSONEachRow',
})

expect(await resultSet.json()).toEqual([ { number: '0' } ])
const resultSet = await client.query({
  query: 'SELECT * from system.numbers LIMIT 1',
  format: 'JSONEachRow',
  clickhouse_settings: { output_format_json_quote_64bit_integers: 0 },
})

expect(await resultSet.json()).toEqual([ { number: 0 } ])

ClickHouse 设置

客户端可以通过 settings 机制调整 ClickHouse 的行为。 这些设置可以在客户端实例级别进行配置,以便应用于发送到 ClickHouse 的每个请求:
const client = createClient({
  clickhouse_settings: {}
})
或者,也可以按请求级别配置某项设置:
client.query({
  clickhouse_settings: {}
})
包含所有受支持的 ClickHouse settings 的类型声明文件可在 此处找到。
请确保发起查询所代表的用户具有足够的权限来更改这些设置。

进阶主题

带参数的查询

您可以创建带参数的查询,并从客户端应用程序向其传递值。这样就无需在客户端使用特定的动态值来格式化 查询。 像平常一样编写查询,然后将希望从应用参数传递给查询的值放在大括号中, 格式如下:
{<name>: <data_type>}
其中:
  • name — 占位符标识符。
  • data_type - 应用参数值的数据类型
示例: 带参数的查询。 源代码
await client.query({
  query: 'SELECT plus({val1: Int32}, {val2: Int32})',
  format: 'CSV',
  query_params: {
    val1: 10,
    val2: 20,
  },
})
更多详情,请参阅 https://clickhouse.com/docs/interfaces/cli#cli-queries-with-parameters-syntax。

压缩

注意:Web 版本目前不支持请求压缩。响应压缩可正常使用。Node.js 版本则同时支持这两者。 对于通过网络传输大型数据集的数据应用,启用压缩会带来明显收益。目前仅支持通过 zlib 使用 GZIP
createClient({
  compression: {
    response: true,
    request: true
  }
})
配置参数如下:
  • response: true 指示 ClickHouse 服务器 返回压缩后的响应体。默认值:response: false
  • request: true 为客户端请求体启用压缩。默认值:request: false

日志 (仅限 Node.js)

日志功能为 Experimental,未来可能会有所变动。
默认的日志记录器实现会通过 console.debug/info 方法将日志记录输出到 stdout,并通过 console.warn/error 方法输出到 stderr。 你可以通过提供 LoggerClass 自定义日志逻辑,并通过 level 参数选择所需的日志级别 (默认为 WARN) :
import type { Logger } from '@clickhouse/client'

// 客户端导出了以下三种 LogParams 类型
interface LogParams {
  module: string
  message: string
  args?: Record<string, unknown>
}
type ErrorLogParams = LogParams & { err: Error }
type WarnLogParams = LogParams & { err?: Error }

class MyLogger implements Logger {
  trace({ module, message, args }: LogParams) {
    // ...
  }
  debug({ module, message, args }: LogParams) {
    // ...
  }
  info({ module, message, args }: LogParams) {
    // ...
  }
  warn({ module, message, args }: WarnLogParams) {
    // ...
  }
  error({ module, message, args, err }: ErrorLogParams) {
    // ...
  }
}

const client = createClient({
  log: {
    LoggerClass: MyLogger,
    level: ClickHouseLogLevel.DEBUG,
  }
})
目前,客户端会记录以下事件:
  • TRACE - 有关 Keep-Alive 套接字生命周期的底层信息
  • DEBUG - 响应信息 (不含授权请求头和主机信息)
  • INFO - 基本上未使用;会在客户端初始化时输出当前日志级别
  • WARN - 非致命错误;失败的 ping 请求会记录为警告,因为底层错误已包含在返回结果中
  • ERROR - query/insert/exec/command 方法中的致命错误,例如请求失败
你可以在这里查看默认的 Logger 实现。

TLS 证书 (仅限 Node.js)

Node.js 客户端可选支持基本 TLS (仅证书颁发机构) 和双向 TLS (证书颁发机构和客户端证书) 。 基本 TLS 配置示例:假设你的证书位于 certs 文件夹中, 且 CA 文件名为 CA.pem
const client = createClient({
  url: 'https://<hostname>:<port>',
  username: '<username>',
  password: '<password>', // 如有需要
  tls: {
    ca_cert: fs.readFileSync('certs/CA.pem'),
  },
})
使用客户端证书的双向 TLS 配置示例:
const client = createClient({
  url: 'https://<hostname>:<port>',
  username: '<username>',
  tls: {
    ca_cert: fs.readFileSync('certs/CA.pem'),
    cert: fs.readFileSync(`certs/client.crt`),
    key: fs.readFileSync(`certs/client.key`),
  },
})
可在代码仓库中查看 基础双向 TLS 的完整示例。

Keep-alive 配置 (仅限 Node.js)

客户端默认会在底层 HTTP agent 中启用 Keep-Alive,这意味着已建立连接的套接字会在后续请求中复用,并发送 Connection: keep-alive 请求头。默认情况下,空闲套接字会在连接池中保留 2500 毫秒 (请参阅有关调整此选项的说明) 。 keep_alive.idle_socket_ttl 的值应明显低于服务器/LB 配置。主要原因是,HTTP/1.1 允许服务器在不通知客户端的情况下关闭套接字,因此如果服务器或负载均衡器在客户端之前关闭了连接,客户端就可能尝试复用已关闭的套接字,从而导致 socket hang up 错误。 如果你要修改 `keep_alive.idle_socket_ttl“,请记住它必须始终与你的服务器/LB Keep-Alive 配置保持一致,并且必须始终低于后者,以确保服务器不会先关闭仍处于打开状态的连接。

调整 idle_socket_ttl

客户端将 keep_alive.idle_socket_ttl 设为 2500 毫秒,因为这通常可视为最稳妥的默认值;在服务端,如果不修改 config.xmlkeep_alive_timeout23.11 之前的 ClickHouse 版本中最低可能只有 3 秒
如果你对当前性能满意且未遇到任何问题,建议不要增大 keep_alive.idle_socket_ttl 的值,因为这可能会导致潜在的 “Socket hang-up” 错误;此外,如果你的应用会发送大量查询,且查询之间的间隔不长,那么默认值通常已经足够,因为这些套接字不会空闲太久,客户端会将它们保留在 pool 中。
你可以运行以下命令,在服务器响应请求头中找到正确的 Keep-Alive 超时值:
curl -is --data-binary "SELECT 1" <clickhouse_url>
查看响应中 ConnectionKeep-Alive 请求头的值。例如:
Connection: Keep-Alive
Keep-Alive: timeout=10
在这种情况下,keep_alive_timeout 为 10 秒,你可以尝试将 keep_alive.idle_socket_ttl 增加到 9000 甚至 9500 毫秒,让空闲套接字保持打开状态的时间比默认情况下稍长一些。请留意可能出现的 “Socket hang-up” 错误,这表明服务器会在客户端之前关闭连接;继续调低该值,直到错误消失。

故障排查

如果即使使用最新版本的客户端,你仍然遇到 socket hang up 错误,可尝试以下方法解决:
  • 启用至少为 WARN 的日志级别 (默认值) 。这样可以检查应用代码中是否存在未消费或悬空的 stream:传输层会以 WARN 级别记录此类情况,因为这可能会导致套接字被服务器关闭。你可以按如下方式在客户端配置中启用日志:
    const client = createClient({
      log: { level: ClickHouseLogLevel.WARN },
    })
    
  • 确保所需配置已应用到正确的客户端实例。如果应用中有多个客户端实例,请再次确认你实际用于查询的那个实例设置了正确的 keep_alive.idle_socket_ttl 值。
  • 将客户端配置中的 keep_alive.idle_socket_ttl 设置减少 500 毫秒。在某些情况下,例如客户端与服务器之间的网络延迟较高时,这样做可能会有帮助,从而避免出现这样的情况:某个发出的请求拿到了一个服务器即将关闭的套接字。
  • 如果该错误发生在长时间运行且没有数据进出的查询期间 (例如长时间运行的 INSERT FROM SELECT) ,这可能是负载均衡器或其他网络组件关闭了长连接或长时间运行的请求所致。你可以尝试组合使用这些 ClickHouse settings,在长时间运行的查询中强制产生一些传入数据:
    const client = createClient({
      // 这里我们假设某些查询的执行时间会超过 5 分钟
      request_timeout: 400_000,
      /** 这些设置配合使用,可以避免长时间运行且没有数据进出的查询场景下的 LB 超时问题,
       *  例如 `INSERT FROM SELECT` 及类似情况,因为连接可能会被 LB 标记为空闲并突然关闭。
       *  在这个例子中,我们假设 LB 的空闲连接超时为 120s,因此将 110s 设为一个“安全”值。 */
      clickhouse_settings: {
        send_progress_in_http_headers: 1,
        http_headers_progress_interval_ms: '110000', // UInt64,应作为字符串传递
      },
    })
    
    不过请注意,在较新的 Node.js 版本中,接收的请求头总大小限制为 16KB;在接收到一定数量的进度请求头后 (在我们的测试中大约是 70-80 个) ,会抛出异常。 也可以采用一种完全不同的方法,彻底避免在线路上的等待时间;这可以利用 HTTP interface 的一个“特性”:连接丢失时,变更不会被取消。更多细节请参见此示例 (第 2 部分)
  • 也可以完全禁用 Keep-Alive 功能。在这种情况下,客户端还会为每个请求添加 Connection: close 请求头,底层 HTTP agent 也不会复用连接。keep_alive.idle_socket_ttl 设置将被忽略,因为不会有空闲套接字。这样会带来额外开销,因为每个请求都需要建立一个新连接。
    const client = createClient({
      keep_alive: {
        enabled: false,
      },
    })
    
  • 可通过一个简单的命令行测试,使用相同的 ClickHouse 实例和相同的网络路径 (即来自同一台机器或同一网段,例如 Kubernetes pod (容器组) ) ,排除包括 Node.js 本身在内的其余网络栈潜在问题,例如使用 curl
    curl -is --user '<user>:<password>' --data-binary "SELECT 1" <clickhouse_url>
    
    你可能需要循环运行几分钟。如果你在 curl 中也看到类似错误,那么问题很可能与客户端配置无关,而是出在网络栈或服务器配置上。
  • 如果要使用原生 Node.js 功能测试连接,你可以尝试使用内置的 fetch API 向 ClickHouse 服务器发起一个简单的 HTTP request:
  const response = await fetch('<clickhouse_url>?query=SELECT+1', {
    method: 'POST',
    headers: {
      'Authorization': 'Basic ' + Buffer.from('<user>:<password>').toString('base64'),
    }
  })
  • 在某些情况下,应用代码或框架适配器可能会在实际执行查询前先发出一次 ping(),这可能导致这样一种情况:ping() 请求成功,但后续查询请求却因空闲连接的同一底层问题而报 “socket hang up” 错误并失败。如果你在日志中看到这种情况,请检查你的框架或应用代码中是否提供了禁用预先 ping() 的选项。这也有助于降低被中间网络组件限流的概率。
  • 确保应用程序本身获得了足够的 CPU 时间,并且网络未被托管提供商限流。GC 暂停指标、事件循环延迟指标等各类监控手段,也有助于排查潜在的资源饥饿问题。
  • 尝试在启用 no-floating-promises ESLint 规则的情况下检查你的应用代码,这有助于识别未处理的 Promise,它们可能导致悬空的流和套接字。

只读用户

使用带有 readonly=1 的用户 的客户端时,无法启用响应压缩,因为这需要 enable_http_compression 设置。以下配置会导致报错:
const client = createClient({
  compression: {
    response: true, // 对 readonly=1 用户不生效
  },
})
请参阅这个示例,其中对 readonly=1 用户的限制有更详细的说明。

带路径名的代理

如果您的 ClickHouse 实例位于代理后方,且其 URL 中包含路径名,例如 http://proxy:8123/clickhouse&#95;server,请将 clickhouse_server 指定为 pathname 配置选项 (可带前导斜杠,也可不带) ;否则,如果直接在 url 中提供,它会被视为 database 选项。支持多段路径,例如 /my_proxy/db
const client = createClient({
  url: 'http://proxy:8123',
  pathname: '/clickhouse_server',
})

带身份验证的反向代理

如果在 ClickHouse 部署前面有一个带身份验证的反向代理,你可以使用 http_headers 设置提供所需的请求头:
const client = createClient({
  http_headers: {
    'My-Auth-Header': '...',
  },
})

自定义 HTTP/HTTPS agent (实验性,仅限 Node.js)

这是一个实验性功能,未来的发行版中可能会发生不向后兼容的变更。客户端提供的默认实现和设置对于大多数场景应该已经足够。只有在你确信确实需要时,才应使用此功能。
默认情况下,客户端会根据客户端配置中提供的设置 (如 max_open_connectionskeep_alive.enabledtls) 来配置底层 HTTP 或 HTTPS agent,由它负责处理与 ClickHouse 服务器 的连接。另外,如果使用了 TLS 证书,底层 agent 也会配置所需证书,并强制使用正确的 TLS 认证请求头。 从 1.2.0 开始,可以为客户端提供自定义 HTTP 或 HTTPS agent,以替换默认的底层 agent。这在网络配置较复杂时会很有用。如果提供了自定义 agent,则需注意以下几点:
  • max_open_connectionstls 选项将_不会生效_,客户端会忽略它们,因为它们属于底层 agent 配置的一部分。
  • keep_alive.enabled 仅控制 Connection 请求头的默认值 (true -> Connection: keep-alivefalse -> Connection: close) 。
  • 空闲 keep-alive 套接字的管理仍会继续生效 (因为它不依赖于 agent,而是依赖于具体的套接字本身) ,但现在也可以通过将 keep_alive.idle_socket_ttl 设置为 0 来完全禁用它。

自定义代理使用示例

不使用证书时使用自定义 HTTP 或 HTTPS 代理:
const agent = new http.Agent({ // 或使用 https.Agent
  keepAlive: true,
  keepAliveMsecs: 2500,
  maxSockets: 10,
  maxFreeSockets: 10,
})
const client = createClient({
  http_agent: agent,
})
使用自定义 HTTPS Agent,并结合基本 TLS 和 CA 证书:
const agent = new https.Agent({
  keepAlive: true,
  keepAliveMsecs: 2500,
  maxSockets: 10,
  maxFreeSockets: 10,
  ca: fs.readFileSync('./ca.crt'),
})
const client = createClient({
  url: 'https://myserver:8443',
  http_agent: agent,
  // 使用自定义 HTTPS agent 时,客户端不会使用默认的 HTTPS 连接实现;请求头需要手动提供
  http_headers: {
    'X-ClickHouse-User': 'username',
    'X-ClickHouse-Key': 'password',
  },
  // 重要:Authorization 请求头会与 TLS 请求头冲突;请禁用它。
  set_basic_auth_header: false,
})
使用启用了双向 TLS 的自定义 HTTPS Agent:
const agent = new https.Agent({
  keepAlive: true,
  keepAliveMsecs: 2500,
  maxSockets: 10,
  maxFreeSockets: 10,
  ca: fs.readFileSync('./ca.crt'),
  cert: fs.readFileSync('./client.crt'),
  key: fs.readFileSync('./client.key'),
})
const client = createClient({
  url: 'https://myserver:8443',
  http_agent: agent,
  // 使用自定义 HTTPS agent 时,客户端将不再使用默认的 HTTPS 连接实现;需要手动提供请求头
  http_headers: {
    'X-ClickHouse-User': 'username',
    'X-ClickHouse-Key': 'password',
    'X-ClickHouse-SSL-Certificate-Auth': 'on',
  },
  // 重要:authorization 请求头会与 TLS 请求头冲突;请将其禁用。
  set_basic_auth_header: false,
})
如果同时使用证书和自定义的 HTTPS Agent,很可能需要通过 set_basic_auth_header 设置 (于 1.2.0 版本中引入) 禁用默认的 Authorization 请求头,因为它会与 TLS 请求头冲突。所有 TLS 请求头都应手动提供。

已知限制 (Node.js/web)

已知限制 (web)

  • select 查询支持流式传输,但 insert 已被禁用 (类型层面也一样) 。
  • 请求压缩已禁用,相关配置会被忽略。响应压缩可用。
  • 尚不支持日志。

性能优化提示

  • 为减少应用程序的内存占用,在适用情况下,可考虑对大型插入 (例如从文件插入) 和查询使用流。对于事件监听器及类似用例,异步插入 也是一个不错的选择,它可以最大限度减少,甚至完全避免在客户端进行批处理。异步插入示例可在客户端代码仓库中找到,文件名前缀为 async_insert_
  • 客户端默认不启用请求或响应压缩。不过,在查询或插入大型数据集时,可以考虑通过 ClickHouseClientConfigOptions.compression 启用它 (仅启用 requestresponse,或同时启用两者) 。
  • 压缩会带来明显的性能开销。分别为 requestresponse 启用压缩,会降低查询或插入的速度,但也会减少应用程序传输的网络流量。

联系我们

如果你有任何问题或需要帮助,欢迎在 Community Slack (#clickhouse-js 频道) 中联系我们,或通过 GitHub issues 与我们取得联系。
最后修改于 2026年6月10日