@clickhouse/client 1.18.5 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@ export { type NodeClickHouseClientConfigOptions as ClickHouseClientConfigOptions
4
4
  export { ResultSet, type StreamReadable } from './result_set';
5
5
  export { drainStream } from './connection/stream';
6
6
  /** Re-export @clickhouse/client-common types */
7
- export { type BaseClickHouseClientConfigOptions, type BaseQueryParams, type QueryParams, type ExecParams, type InsertParams, type InsertValues, type CommandParams, type CommandResult, type ExecResult, type InsertResult, type DataFormat, type RawDataFormat, type JSONDataFormat, type StreamableDataFormat, type StreamableJSONDataFormat, type SingleDocumentJSONFormat, type Logger, type LogParams, type ErrorLogParams, type WarnLogParams, type ClickHouseSettings, type MergeTreeSettings, type Row, type ResponseJSON, type InputJSON, type InputJSONObjectEachRow, type BaseResultSet, type PingResult, ClickHouseError, parseError, ClickHouseLogLevel, SettingsMap, SupportedJSONFormats, SupportedRawFormats, StreamableFormats, StreamableJSONFormats, SingleDocumentJSONFormats, RecordsJSONFormats, type SimpleColumnType, type ParsedColumnSimple, type ParsedColumnEnum, type ParsedColumnFixedString, type ParsedColumnNullable, type ParsedColumnDecimal, type ParsedColumnDateTime, type ParsedColumnDateTime64, type ParsedColumnArray, type ParsedColumnTuple, type ParsedColumnMap, type ParsedColumnType, parseColumnType, SimpleColumnTypes, type ProgressRow, isProgressRow, isRow, isException, type RowOrProgress, type ClickHouseAuth, type ClickHouseJWTAuth, type ClickHouseCredentialsAuth, TupleParam, } from '@clickhouse/client-common';
7
+ export { type BaseClickHouseClientConfigOptions, type BaseQueryParams, type QueryParams, type ExecParams, type InsertParams, type InsertValues, type CommandParams, type CommandResult, type ExecResult, type InsertResult, type DataFormat, type RawDataFormat, type JSONDataFormat, type StreamableDataFormat, type StreamableJSONDataFormat, type SingleDocumentJSONFormat, type Logger, type LogParams, type ErrorLogParams, type WarnLogParams, type ClickHouseSettings, type MergeTreeSettings, type Row, type ResponseJSON, type InputJSON, type InputJSONObjectEachRow, type BaseResultSet, type PingResult, type ResponseHeaders, ClickHouseError, parseError, ClickHouseLogLevel, SettingsMap, SupportedJSONFormats, SupportedRawFormats, StreamableFormats, StreamableJSONFormats, SingleDocumentJSONFormats, RecordsJSONFormats, type SimpleColumnType, type ParsedColumnSimple, type ParsedColumnEnum, type ParsedColumnFixedString, type ParsedColumnNullable, type ParsedColumnDecimal, type ParsedColumnDateTime, type ParsedColumnDateTime64, type ParsedColumnArray, type ParsedColumnTuple, type ParsedColumnMap, type ParsedColumnType, parseColumnType, SimpleColumnTypes, type ProgressRow, isProgressRow, isRow, isException, type RowOrProgress, type ClickHouseAuth, type ClickHouseJWTAuth, type ClickHouseCredentialsAuth, TupleParam, } from '@clickhouse/client-common';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAGiB;AAFf,0GAAA,oBAAoB,OAAoB;AAG1C,mCAAuC;AAA9B,sGAAA,YAAY,OAAA;AAErB,2CAA6D;AAApD,uGAAA,SAAS,OAAA;AAClB,8CAAiD;AAAxC,qGAAA,WAAW,OAAA;AAEpB,gDAAgD;AAChD,2DA8DkC;AAjChC,gHAAA,eAAe,OAAA;AACf,2GAAA,UAAU,OAAA;AACV,mHAAA,kBAAkB,OAAA;AAClB,4GAAA,WAAW,OAAA;AACX,qHAAA,oBAAoB,OAAA;AACpB,oHAAA,mBAAmB,OAAA;AACnB,kHAAA,iBAAiB,OAAA;AACjB,sHAAA,qBAAqB,OAAA;AACrB,0HAAA,yBAAyB,OAAA;AACzB,mHAAA,kBAAkB,OAAA;AAalB,gHAAA,eAAe,OAAA;AACf,kHAAA,iBAAiB,OAAA;AAEjB,8GAAA,aAAa,OAAA;AACb,sGAAA,KAAK,OAAA;AACL,4GAAA,WAAW,OAAA;AAKX,2GAAA,UAAU,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAGiB;AAFf,0GAAA,oBAAoB,OAAoB;AAG1C,mCAAuC;AAA9B,sGAAA,YAAY,OAAA;AAErB,2CAA6D;AAApD,uGAAA,SAAS,OAAA;AAClB,8CAAiD;AAAxC,qGAAA,WAAW,OAAA;AAEpB,gDAAgD;AAChD,2DA+DkC;AAjChC,gHAAA,eAAe,OAAA;AACf,2GAAA,UAAU,OAAA;AACV,mHAAA,kBAAkB,OAAA;AAClB,4GAAA,WAAW,OAAA;AACX,qHAAA,oBAAoB,OAAA;AACpB,oHAAA,mBAAmB,OAAA;AACnB,kHAAA,iBAAiB,OAAA;AACjB,sHAAA,qBAAqB,OAAA;AACrB,0HAAA,yBAAyB,OAAA;AACzB,mHAAA,kBAAkB,OAAA;AAalB,gHAAA,eAAe,OAAA;AACf,kHAAA,iBAAiB,OAAA;AAEjB,8GAAA,aAAa,OAAA;AACb,sGAAA,KAAK,OAAA;AACL,4GAAA,WAAW,OAAA;AAKX,2GAAA,UAAU,OAAA"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "1.18.5";
1
+ declare const _default: "1.19.0";
2
2
  export default _default;
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = '1.18.5';
3
+ exports.default = '1.19.0';
4
4
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@clickhouse/client",
3
3
  "description": "Official JS client for ClickHouse DB - Node.js implementation",
4
4
  "homepage": "https://clickhouse.com",
5
- "version": "1.18.5",
5
+ "version": "1.19.0",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
8
8
  "clickhouse",
@@ -44,7 +44,7 @@
44
44
  "build": "rm -rf dist; tsc"
45
45
  },
46
46
  "dependencies": {
47
- "@clickhouse/client-common": "1.18.5"
47
+ "@clickhouse/client-common": "1.19.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "simdjson": "^0.9.2"
@@ -5,14 +5,8 @@ description: >
5
5
  (`@clickhouse/client`). Use this skill whenever a user is *building* against
6
6
  the Node.js client — configuring the client, pinging, inserting rows in JSON
7
7
  or raw formats, selecting and parsing results, binding query parameters,
8
- managing sessions and temporary tables, working with data types like
9
- `Date`/`DateTime`/`Decimal`/`Time`/`Time64`/`Dynamic`/`Variant`/`JSON`, or
10
- customizing JSON parsing. Trigger on phrases like "how do I insert…", "how
11
- do I select…", "what format should I use…", "how do I parameterize…", "how
12
- do I configure the client…". Do NOT use for browser/Web client code, for
13
- performance/streaming/Parquet questions (see `examples/node/performance/`),
14
- or for diagnosing errors and unexpected behavior (see
15
- clickhouse-js-node-troubleshooting).
8
+ managing sessions and temporary tables, working with data types or
9
+ customizing JSON parsing. Do NOT use for browser/Web client code.
16
10
  ---
17
11
 
18
12
  # ClickHouse Node.js Client — Coding
@@ -37,9 +31,9 @@ Reference: https://clickhouse.com/docs/integrations/javascript
37
31
  each relevant item; those checklists capture details users usually need but
38
32
  are easy to omit in short answers.
39
33
  2. **Always import from `@clickhouse/client`** (never `@clickhouse/client-web`)
40
- and create a single client with `createClient({ url })` or rely on
34
+ and create a client with `createClient({ url })` or rely on
41
35
  supported defaults when appropriate. Close it with `await client.close()`
42
- during graceful shutdown.
36
+ preferably when it's no longer needed or during graceful shutdown for global resources.
43
37
  3. **Prefer `JSONEachRow` for typical row inserts/selects** unless the user
44
38
  has already chosen another format or is streaming raw bytes (CSV / TSV /
45
39
  Parquet — see `examples/node/performance/`).
@@ -49,6 +43,12 @@ Reference: https://clickhouse.com/docs/integrations/javascript
49
43
  Always mention this when the user configures settings at the client level.
50
44
  4. **Always use `query_params` for user-supplied values** — never template-
51
45
  literal-interpolate them into SQL. See `reference/query-parameters.md`.
46
+ **When answering a parameter-binding question, your response must
47
+ explicitly name template-literal interpolation as a "SQL injection
48
+ risk"** — even when the user only asked about syntax and did not raise
49
+ security. The literal phrase "SQL injection" needs to appear; this is
50
+ the most common mistake from PostgreSQL/MySQL users and the security
51
+ framing is part of the correct answer, not an optional aside.
52
52
  5. **Pick the right method for the job:**
53
53
  - `client.insert()` — write rows.
54
54
  - `client.query()` + `resultSet.json()` / `.text()` / `.stream()` — read
@@ -67,10 +67,6 @@ Reference: https://clickhouse.com/docs/integrations/javascript
67
67
  - `Time` / `Time64` data types: ClickHouse server `>= 25.6`.
68
68
  - `Dynamic` / `Variant` / new `JSON` types: ClickHouse server `>= 24.1` /
69
69
  `24.5` / `24.8` (no longer experimental since `25.3`).
70
- 7. **Show a runnable snippet**, not pseudo-code. The examples in
71
- [`examples/node/coding/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/coding)
72
- are all self-contained and runnable against the repo's `docker-compose up`
73
- setup — pattern your snippet after them.
74
70
 
75
71
  ---
76
72
 
@@ -97,16 +93,13 @@ Identify the user's task and read the matching reference file.
97
93
  ## Conventions used in answers
98
94
 
99
95
  - Always show `import { createClient } from '@clickhouse/client'` (Node, never
100
- Web). For things that require a runtime API, prefer `node:` built-ins
101
- (e.g., `import * as crypto from 'node:crypto'`).
96
+ Web).
102
97
  - Always `await client.close()` at the end of self-contained snippets; in
103
98
  long-running services, close on graceful shutdown.
104
- - Prefer top-level `await` in snippets to match the style of
105
- `examples/node/coding/*.ts`.
106
99
  - For inserts, prefer `format: 'JSONEachRow'` and `values: [...]` unless the
107
100
  user's scenario requires otherwise.
108
101
  - For selects, prefer `await (await client.query({...})).json<RowType>()` for
109
- small / medium result sets; for streaming, see `examples/node/performance/`.
102
+ small / medium result sets; for bigger results suggest streaming.
110
103
  - When showing parameter binding, use ClickHouse's native `{name: Type}`
111
104
  syntax — never `$1`, `?`, or `:name`.
112
105
  - For DDL inside a cluster or behind a load balancer, set
@@ -3,9 +3,6 @@
3
3
  > **Applies to:** all client versions; the relevant settings are server-side.
4
4
  > See https://clickhouse.com/docs/en/optimize/asynchronous-inserts.
5
5
 
6
- Backing example:
7
- [`examples/node/coding/async_insert.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/async_insert.ts).
8
-
9
6
  > **When to use async inserts:** when many small inserts arrive concurrently
10
7
  > (e.g., one per HTTP request) and you don't want to maintain a client-side
11
8
  > batching layer. ClickHouse will batch them server-side. This is also the
@@ -13,12 +10,11 @@ Backing example:
13
10
 
14
11
  > **When _not_ to use async inserts:** when you already build large batches
15
12
  > client-side (e.g., from a stream). Plain inserts are simpler and lower
16
- > latency. For raw throughput tuning of large async-insert workloads, see
17
- > [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance).
13
+ > latency.
18
14
 
19
15
  ## Setup
20
16
 
21
- Enable on the client (or per-request) via `clickhouse_settings`:
17
+ Enable on the client level or per-request via `clickhouse_settings`:
22
18
 
23
19
  ```ts
24
20
  import { createClient, ClickHouseError } from '@clickhouse/client'
@@ -89,6 +85,9 @@ await client.command({
89
85
  })
90
86
  ```
91
87
 
88
+ Even better is to create a specialized client for inserts with the appropriate async
89
+ settings and a separate client for DDL and other queries.
90
+
92
91
  ## Common pitfalls
93
92
 
94
93
  - **Setting `async_insert` per call but expecting client-side batching.**
@@ -101,3 +100,8 @@ await client.command({
101
100
  failures will not surface to the client.
102
101
  - **Not handling `ClickHouseError`.** It exposes `err.code`, which maps to
103
102
  rows in the `system.errors` table — use it to decide whether to retry.
103
+
104
+ ## See also
105
+
106
+ - For raw throughput tuning of large async-insert workloads,
107
+ see [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance).
@@ -6,11 +6,6 @@
6
6
  > - `clickhouse_setting_*` / `ch_*` URL parameters: client `>= 1.0.0`.
7
7
  > - `keep_alive.idle_socket_ttl` (Node-only): client `>= 1.0.0`.
8
8
 
9
- Backing examples:
10
- [`examples/node/coding/url_configuration.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/url_configuration.ts),
11
- [`examples/node/coding/clickhouse_settings.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/clickhouse_settings.ts),
12
- [`examples/node/coding/default_format_setting.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/default_format_setting.ts).
13
-
14
9
  ## Answer checklist
15
10
 
16
11
  When answering configuration questions, include the relevant points:
@@ -53,22 +48,7 @@ await client.close()
53
48
  http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]
54
49
  ```
55
50
 
56
- ## Configuration via URL parameters
57
-
58
- A fixed allowlist of config fields can be set as URL query parameters
59
- (plus any key prefixed with `clickhouse_setting_` / `ch_` / `http_header_`).
60
- **Supported URL parameters override the corresponding values in the rest of
61
- the configuration object** — when they do, the client logs a warning.
62
- Unknown URL parameters cause `createClient` to throw
63
- `Unknown URL parameters: ...`
64
- (see [`packages/client-common/src/config.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/packages/client-common/src/config.ts) for the shared allowlist, and [`packages/client-node/src/config.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/packages/client-node/src/config.ts) for Node-specific URL parameters).
65
-
66
- Supported non-prefixed keys parsed by `client-common`: `application`,
67
- `session_id`, `pathname`, `access_token`, `request_timeout`,
68
- `max_open_connections`, `compression_request`, `compression_response`,
69
- `log_level`, `keep_alive_enabled`. Additionally, Node supports
70
- `keep_alive_idle_socket_ttl` via the Node-specific config implementation.
71
- Anything else must be passed via the config object on `createClient`.
51
+ ## Configuration via URL
72
52
 
73
53
  Prefer explicit object fields in application code. Use the URL form when the
74
54
  application receives one connection string from an environment variable, secret
@@ -77,7 +57,7 @@ source code — show it as a shell export and read it in Node:
77
57
 
78
58
  ```bash
79
59
  # In your shell environment / deployment config (e.g. .env, Kubernetes secret):
80
- export CLICKHOUSE_URL='https://bob:secret@my.host:8124/analytics?application=my_analytics_app&ch_async_insert=1&ch_wait_for_async_insert=0'
60
+ export CLICKHOUSE_URL='https://bob:secret@my.host:8124/analytics'
81
61
  ```
82
62
 
83
63
  ```ts
@@ -85,21 +65,6 @@ export CLICKHOUSE_URL='https://bob:secret@my.host:8124/analytics?application=my_
85
65
  const client = createClient({ url: process.env.CLICKHOUSE_URL })
86
66
  ```
87
67
 
88
- The same connection can also be expressed as an explicit config object (useful when you want to document each field individually):
89
-
90
- ```ts
91
- import { createClient } from '@clickhouse/client'
92
-
93
- createClient({
94
- url: 'https://my.host:8124',
95
- username: 'bob',
96
- password: 'secret',
97
- database: 'analytics',
98
- application: 'my_analytics_app',
99
- clickhouse_settings: { async_insert: 1, wait_for_async_insert: 0 },
100
- })
101
- ```
102
-
103
68
  ## Per-client vs per-request `clickhouse_settings` ⭐
104
69
 
105
70
  > **Always mention this when discussing `clickhouse_settings`:** settings set
@@ -112,13 +77,12 @@ for **that call only**.
112
77
  ```ts
113
78
  const client = createClient({
114
79
  clickhouse_settings: {
115
- date_time_input_format: 'best_effort', // applied to every request
80
+ output_format_json_quote_64bit_integers: 0, // applied to every request
116
81
  },
117
82
  })
118
83
 
119
84
  const rows = await client.query({
120
- query: 'SELECT number FROM system.numbers LIMIT 2',
121
- format: 'JSONEachRow',
85
+ query: 'SELECT number FROM system.numbers LIMIT 2 FORMAT JSONEachRow',
122
86
  clickhouse_settings: {
123
87
  output_format_json_quote_64bit_integers: 1, // overrides client default for this call
124
88
  },
@@ -155,5 +119,5 @@ only needed for raw `exec()`.
155
119
  for the DB. (Symptom: "wrong database selected.") See the
156
120
  troubleshooting skill for diagnosis.
157
121
  - **Don't create a client per request.** `createClient` opens a connection
158
- pool; share one client across the process and `close()` on shutdown.
122
+ pool; share one client across requests and `close()` on shutdown.
159
123
  - **`max_open_connections` must be `>= 1`** when set explicitly.
@@ -3,9 +3,6 @@
3
3
  > **Requires:** client `>= 1.14.0` (configurable `json.parse` and
4
4
  > `json.stringify`). Earlier versions cannot swap the JSON implementation.
5
5
 
6
- Backing example:
7
- [`examples/node/coding/custom_json_handling.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/custom_json_handling.ts).
8
-
9
6
  ## Answer checklist
10
7
 
11
8
  When the user wants `UInt64`/`Int64` values back as `BigInt`:
@@ -66,7 +63,7 @@ const valueSerializer = (value: unknown): unknown => {
66
63
 
67
64
  const client = createClient({
68
65
  json: {
69
- parse: JSON.parse,
66
+ parse: JSON.parse, // use default parsing
70
67
  stringify: (obj: unknown) => JSON.stringify(valueSerializer(obj)),
71
68
  },
72
69
  })
@@ -85,17 +82,12 @@ await client.insert({
85
82
  format: 'JSONEachRow',
86
83
  values: [
87
84
  {
88
- id: BigInt(250000000000000200), // serialized as a string
85
+ id: BigInt('250000000000000200'), // serialized as a string
89
86
  dt: new Date(), // serialized as ms since epoch
90
87
  },
91
88
  ],
92
89
  })
93
90
 
94
- const rows = await client.query({
95
- query: 'SELECT * FROM inserts_custom_json_handling',
96
- format: 'JSONEachRow',
97
- })
98
- console.info(await rows.json())
99
91
  await client.close()
100
92
  ```
101
93
 
@@ -126,16 +118,65 @@ const client = createClient({
126
118
  })
127
119
  ```
128
120
 
129
- This applies to **both** outgoing JSON bodies and incoming JSON-format
130
- responses. Combine with `output_format_json_quote_64bit_integers: 0` (the
131
- default since CH 25.8) so the server emits unquoted 64-bit integers that
132
- `json-bigint` can parse to `BigInt`.
121
+ `output_format_json_quote_64bit_integers: 0` is the default since
122
+ ClickHouse `25.8`; setting it explicitly is useful for older servers and
123
+ makes the example self-contained. With it off, the server emits unquoted
124
+ 64-bit integers that `json-bigint` parses straight to `BigInt`. The
125
+ `json` option applies to **both** outgoing JSON bodies and incoming
126
+ JSON-format responses.
127
+
128
+ ## Recipe: Zero-dep BigInt parsing (no `npm install`)
129
+
130
+ If adding a dependency is awkward (locked lockfile, restricted environment,
131
+ or you just don't want to pull in `json-bigint`), you can plug in a
132
+ hand-rolled reviver. This uses the `context.source` argument that
133
+ `JSON.parse` revivers gained in Node `>= 20.16` / `>= 21.7`, so the raw
134
+ numeric literal is available before it's coerced to a JS `number`:
135
+
136
+ ```ts
137
+ import { createClient } from '@clickhouse/client'
138
+
139
+ const parseBigInt = (text: string) =>
140
+ JSON.parse(text, function (key, value, context) {
141
+ if (key.endsWith('__bigint')) {
142
+ return BigInt(context.source)
143
+ }
144
+ return value
145
+ })
146
+
147
+ const client = createClient({
148
+ json: {
149
+ parse: parseBigInt,
150
+ stringify: JSON.stringify, // use default stringify
151
+ },
152
+ clickhouse_settings: { output_format_json_quote_64bit_integers: 0 },
153
+ })
154
+
155
+ const rs = await client.query({
156
+ query: 'SELECT toUInt64(250000000000000200) AS id__bigint',
157
+ })
158
+ const { data } = await rs.json()
159
+ console.log(data[0].id__bigint) // 250000000000000200
160
+
161
+ await client.close()
162
+ ```
163
+
164
+ Trade-offs versus `json-bigint`:
165
+
166
+ - ✓ No new dependency to install.
167
+ - ✓ Only promotes known to be 64-bit integers to `BigInt`.
168
+ - ✗ Requires Node `>= 20.16` / `>= 21.7` for the reviver `context.source`.
169
+ On older Node, prefer `json-bigint` or upgrade Node.
170
+ - ✗ Outgoing `stringify` still uses default `JSON.stringify`, which throws
171
+ on `BigInt`. Pair with the `valueSerializer` pattern above if your
172
+ inserts contain `BigInt` values.
133
173
 
134
174
  ## Common pitfalls
135
175
 
136
176
  - **Setting `json.parse` only.** That only affects reading JSON responses;
137
177
  outgoing JSON bodies use `json.stringify`. If you want consistent custom
138
- handling in both directions, generally provide a matching `stringify` too.
178
+ handling in both directions, generally provide a matching `stringify` too
179
+ or a throwing serializer that prevents mismatches.
139
180
  - **Forgetting `bigint` handling in `stringify`.** Default `JSON.stringify`
140
181
  throws on `BigInt`; if your data ever contains one, the insert will fail
141
182
  with `TypeError: Do not know how to serialize a BigInt`.
@@ -147,3 +188,6 @@ default since CH 25.8) so the server emits unquoted 64-bit integers that
147
188
  are silently rounded. Do **not** try to fix precision loss by calling
148
189
  `Number()`, `parseInt()`, or `parseFloat()` on the value. The correct fix
149
190
  is a `BigInt`-aware parser (shown above), not a lossy cast.
191
+ - **Mixing BigInt and number for the same column.** If some values are `BigInt` and
192
+ others are `number`, your app code needs to handle both types. Otherwise
193
+ JavaScript will throw a `TypeError: Cannot mix BigInt and other types`.
@@ -10,10 +10,6 @@
10
10
  > - `Time` / `Time64`: ClickHouse `>= 25.6` and require
11
11
  > `enable_time_time64_type: 1`.
12
12
 
13
- Backing examples:
14
- [`examples/node/coding/dynamic_variant_json.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/dynamic_variant_json.ts),
15
- [`examples/node/coding/time_time64.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/time_time64.ts).
16
-
17
13
  ## Answer checklist
18
14
 
19
15
  When answering about storing and reading JSON objects:
@@ -21,6 +17,29 @@ When answering about storing and reading JSON objects:
21
17
  - Use the new `JSON` column type, introduced in ClickHouse `>= 24.8`.
22
18
  - Say `JSON` is no longer experimental since ClickHouse `25.3`; on older
23
19
  supported versions, enable `allow_experimental_json_type`.
20
+ - **State the version policy in your explanation AND inline in the code.**
21
+ When you set `allow_experimental_json_type` (or any `allow_experimental_*_type`)
22
+ in code, you must do BOTH of the following:
23
+ 1. Put an inline comment directly above the setting that names the version
24
+ where the type was introduced and the version where it became
25
+ non-experimental. The comment is the durable version provenance — it
26
+ lives in the user's source file long after the chat reply is gone.
27
+ 2. Repeat the version policy in your prose reply.
28
+
29
+ For the `JSON` column type the inline comment must look like:
30
+
31
+ ```ts
32
+ clickhouse_settings: {
33
+ // JSON type introduced in ClickHouse 24.8, non-experimental since 25.3.
34
+ // This setting is required only on 24.8–25.2; harmless on >= 25.3.
35
+ allow_experimental_json_type: 1,
36
+ }
37
+ ```
38
+
39
+ Without the inline comment, a reader on a newer server has no idea the
40
+ setting is a no-op and a reader on an older server has no idea why it's
41
+ required.
42
+
24
43
  - Insert real JS objects with `format: 'JSONEachRow'`; do not
25
44
  `JSON.stringify()` the column value.
26
45
  - Read with a JSON output format such as `JSONEachRow` and `resultSet.json()`;
@@ -32,8 +51,10 @@ When answering about storing and reading JSON objects:
32
51
  import { createClient } from '@clickhouse/client'
33
52
 
34
53
  const client = createClient({
35
- // Required only on ClickHouse < 25.3 — harmless to leave on
36
54
  clickhouse_settings: {
55
+ // Variant introduced in 24.1, Dynamic in 24.5, JSON in 24.8.
56
+ // All three are non-experimental since 25.3; these settings are
57
+ // required only on 24.1–25.2 and are harmless on >= 25.3.
37
58
  allow_experimental_variant_type: 1,
38
59
  allow_experimental_dynamic_type: 1,
39
60
  allow_experimental_json_type: 1,
@@ -77,6 +98,33 @@ const rs = await client.query({
77
98
  console.log(await rs.json())
78
99
  ```
79
100
 
101
+ Outputs:
102
+
103
+ ```js
104
+ ;[
105
+ {
106
+ id: '1',
107
+ var: '42',
108
+ dynamic: 'foo',
109
+ json: { foo: 'x' },
110
+ 'variantType(var)': 'Int64',
111
+ 'dynamicType(dynamic)': 'String',
112
+ 'dynamicType(json.foo)': 'String',
113
+ 'dynamicType(json.bar)': 'None',
114
+ },
115
+ {
116
+ id: '2',
117
+ var: 'str',
118
+ dynamic: '144',
119
+ json: { bar: '10' },
120
+ 'variantType(var)': 'String',
121
+ 'dynamicType(dynamic)': 'Int64',
122
+ 'dynamicType(json.foo)': 'None',
123
+ 'dynamicType(json.bar)': 'Int64',
124
+ },
125
+ ]
126
+ ```
127
+
80
128
  ### Notes
81
129
 
82
130
  - The `JSON` column type accepts a real JS object on insert and returns one
@@ -154,7 +202,8 @@ await client.insert({
154
202
  - Pass values as **strings** in the `HH:MM:SS[.fraction]` format. Negatives
155
203
  are supported; the magnitude can exceed 24 hours.
156
204
  - For `Time64(p)` with `p > 3`, do not use JS `Date` — it tops out at
157
- millisecond precision and will silently truncate.
205
+ millisecond precision and will silently truncate. Store nanosecond values
206
+ separately and provide on stringify as needed.
158
207
 
159
208
  ## Common pitfalls
160
209
 
@@ -167,3 +216,6 @@ await client.insert({
167
216
  - **Reading a `Variant`/`Dynamic` value of type `Int64` and being surprised
168
217
  it's a string.** That's the standard 64-bit-integers-in-JSON behavior;
169
218
  see the troubleshooting skill if you need to change it.
219
+ - **Avoid parsing Variant/Dynamic/JSON columns that mix strings and 64-bit**
220
+ without checking their returned types first. Otherwise a number stored in
221
+ a string will come back as a number or vice versa.
@@ -3,12 +3,6 @@
3
3
  > **Applies to:** all versions. The `columns` option (both forms) and the
4
4
  > `database` config field are universally supported.
5
5
 
6
- Backing examples:
7
- [`examples/node/coding/insert_specific_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_specific_columns.ts),
8
- [`examples/node/coding/insert_exclude_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_exclude_columns.ts),
9
- [`examples/node/coding/insert_ephemeral_columns.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_ephemeral_columns.ts),
10
- [`examples/node/coding/insert_into_different_db.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_into_different_db.ts).
11
-
12
6
  ## Answer checklist
13
7
 
14
8
  When explaining partial-column inserts:
@@ -29,9 +23,9 @@ get their declared default.
29
23
  ```ts
30
24
  await client.insert({
31
25
  table: 'events',
26
+ columns: ['message'], // the rest of the events table columns get their DEFAULTs
32
27
  format: 'JSONEachRow',
33
28
  values: [{ message: 'foo' }],
34
- columns: ['message'], // `id` will get its default (0 for UInt32)
35
29
  })
36
30
  ```
37
31
 
@@ -4,14 +4,8 @@
4
4
  > ClickHouse feature; the JSON _formats_ listed here are universally supported
5
5
  > by the client.
6
6
 
7
- Backing examples:
8
- [`examples/node/coding/array_json_each_row.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/array_json_each_row.ts),
9
- [`examples/node/coding/insert_data_formats_overview.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_data_formats_overview.ts).
10
-
11
7
  > **Raw / binary formats (CSV, TSV, CustomSeparated, Parquet) require a Node
12
- > stream as input.** See
13
- > [`examples/node/performance/`](https://github.com/ClickHouse/clickhouse-js/tree/main/examples/node/performance)
14
- > — defer if the user wants to insert from a file or `Readable`.
8
+ > stream as input.** Suggest streaming when the user wants to insert from a file or `Readable`.
15
9
 
16
10
  ## Answer checklist
17
11
 
@@ -125,12 +119,12 @@ await client.insert({
125
119
 
126
120
  ## Quick chooser
127
121
 
128
- | Use case | Format |
129
- | -------------------------------------------- | ------------------------------------------------- |
130
- | Insert plain JS objects | `JSONEachRow` _(default)_ |
131
- | Insert tuples / column-positional rows | `JSONCompactEachRow` |
132
- | Insert with explicit column ordering / types | `JSONCompactEachRow*WithNames…` |
133
- | Insert a single document with metadata | `JSON`, `JSONCompact` |
122
+ | Use case | Format |
123
+ | -------------------------------------------- | ------------------------------------------------------- |
124
+ | Insert plain JS objects | `JSONEachRow` _(default)_ |
125
+ | Insert tuples / column-positional rows | `JSONCompactEachRow` |
126
+ | Insert with explicit column ordering / types | `JSONCompactEachRow*WithNames…` |
127
+ | Insert a single document with metadata | `JSON`, `JSONCompact` |
134
128
  | Insert from a CSV / TSV / Parquet file | Raw format + Node stream → `examples/node/performance/` |
135
129
 
136
130
  ## Common pitfalls
@@ -143,3 +137,6 @@ await client.insert({
143
137
  `JSONColumnsWithMetadata`).
144
138
  - For type guidance (`Decimal` strings, `Date` objects, `BigInt`), see
145
139
  `insert-values.md` and `custom-json.md`.
140
+ - **Use runtime type checkers like `zod` or `io-ts` if your app ingests untrusted JSON.**
141
+ It's easier to debug mismatches between your data and the format's expected shape with a validation library used at the place of ingestion than with ClickHouse errors.
142
+ This is especially true in the middle of a large insert batch or streaming operation.
@@ -3,12 +3,6 @@
3
3
  > **Applies to:** all versions. `wait_end_of_query: 1` is a server-side
4
4
  > setting available on every supported ClickHouse version.
5
5
 
6
- Backing examples:
7
- [`examples/node/coding/insert_from_select.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_from_select.ts),
8
- [`examples/node/coding/insert_values_and_functions.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_values_and_functions.ts),
9
- [`examples/node/coding/insert_js_dates.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_js_dates.ts),
10
- [`examples/node/coding/insert_decimals.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/insert_decimals.ts).
11
-
12
6
  ## `INSERT … SELECT` (no values payload)
13
7
 
14
8
  When the data already lives in ClickHouse, use `client.command()` with a raw
@@ -18,7 +12,7 @@ When the data already lives in ClickHouse, use `client.command()` with a raw
18
12
  await client.command({
19
13
  query: `
20
14
  INSERT INTO target
21
- SELECT '42', quantilesBFloat16State(0.5)(arrayJoin([toFloat32(10), toFloat32(20)]))
15
+ SELECT * FROM source
22
16
  `,
23
17
  })
24
18
  ```
@@ -71,7 +65,7 @@ await client.insert({
71
65
  format: 'JSONEachRow',
72
66
  values: [{ id: '42', dt: new Date() }],
73
67
  clickhouse_settings: {
74
- date_time_input_format: 'best_effort',
68
+ date_time_input_format: 'best_effort', // default on the Cloud
75
69
  },
76
70
  })
77
71
  ```
@@ -81,6 +75,13 @@ await client.insert({
81
75
 
82
76
  ## Inserting `Decimal*` values
83
77
 
78
+ **IMPORTANT:** Make sure that the application code you're working on or the user
79
+ prompt clearly indicates that floats are not used anywhere for decimal values.
80
+ The most common scenario is using floats for money amounts in the app while the
81
+ database uses `Decimal` for them. In that case, the app code should be changed
82
+ to use a proper decimal library and serialization strategy (custom serializer
83
+ or a class using `toJSON()`) to `string` instead of JS `number`.
84
+
84
85
  Decimals must be passed as **strings** in JSON formats to avoid precision
85
86
  loss in JavaScript:
86
87
 
@@ -130,8 +131,6 @@ const rs = await client.query({
130
131
 
131
132
  ## Common pitfalls
132
133
 
133
- - **Passing decimals as JS `number`s.** Anything beyond `Number.MAX_SAFE_INTEGER`
134
- silently loses precision before it ever reaches the server.
135
134
  - **Using `client.insert()` for `INSERT … SELECT`.** There's nothing to
136
135
  upload — use `client.command()` with the full SQL.
137
136
  - **Forgetting `date_time_input_format: 'best_effort'`** when inserting
@@ -139,3 +138,4 @@ const rs = await client.query({
139
138
  ISO-8601 with the `T`/`Z` separators.
140
139
  - **Hand-building `VALUES` with user input.** Always parameterize user data;
141
140
  see `reference/query-parameters.md`.
141
+ - **Using floats in the app and expect `Decimal` columns to store them safely.** Use a proper decimal library and pass them as strings to avoid precision loss.
@@ -1,12 +1,29 @@
1
1
  # Ping the Server
2
2
 
3
- > **Applies to:** all versions. `ping()` returns a discriminated
3
+ > **Applies to:** all versions. `ping()` returns a discriminated union
4
4
  > `PingResult = { success: true } | { success: false, error: Error }` —
5
5
  > it does **not** throw on connection failures.
6
6
 
7
- Backing examples:
8
- [`examples/node/coding/ping_existing_host.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/ping_existing_host.ts),
9
- [`examples/node/coding/ping_non_existing_host.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/ping_non_existing_host.ts).
7
+ ## Answer checklist
8
+
9
+ When answering "how do I health-check / readiness-probe ClickHouse?":
10
+
11
+ - Use `await client.ping()` (or `ping({ select: true })`) and branch on
12
+ `result.success` directly — **do not** wrap in `try/catch` as the only
13
+ check, and do not substitute `query('SELECT 1')`.
14
+ - For a readiness probe / "can it serve traffic", recommend
15
+ `client.ping({ select: true })` so credentials and the query layer are
16
+ validated, not just the socket.
17
+ - **Always contrast the two forms explicitly in your answer**, even when
18
+ you're recommending one: plain `client.ping()` hits `/ping` (TCP/HTTP
19
+ reachability only — does not validate credentials or query processing);
20
+ `client.ping({ select: true })` issues a lightweight `SELECT 1` (validates
21
+ auth and query path). Name both and say which to use for liveness vs
22
+ readiness.
23
+ - Recommend lowering `request_timeout` on the client used for probes so
24
+ they fail fast instead of hanging on the default timeout — pick a value
25
+ comparable to the probe interval (e.g., `1500`–`2000` ms for a
26
+ 2-second-interval probe).
10
27
 
11
28
  ## Successful ping
12
29
 
@@ -118,3 +135,7 @@ const r = await client.ping({ select: true })
118
135
  - **Plain `ping()` does not check credentials.** If auth is part of what you
119
136
  want to verify, use `ping({ select: true })`.
120
137
  - For ping that times out specifically, see the troubleshooting skill.
138
+ - **Only ping the ClickHouse server in your app's liveness probe** if the app
139
+ has to be restarted to recover from a ClickHouse outage. If the app can recover
140
+ the connection to ClickHouse without a restart, put the ping in a readiness
141
+ probe instead so the app doesn't get killed unnecessarily.
@@ -6,17 +6,16 @@
6
6
  > `Array`/`Tuple`/`Map` parameters fixed in `>= 1.13.0`. `BigInt` query
7
7
  > parameters `>= 1.15.0`.
8
8
 
9
- Backing examples:
10
- [`examples/node/coding/query_with_parameter_binding.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/query_with_parameter_binding.ts),
11
- [`examples/node/coding/query_with_parameter_binding_special_chars.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/query_with_parameter_binding_special_chars.ts).
12
-
13
9
  ## Answer checklist
14
10
 
15
11
  When the user passes user-controlled values into SQL:
16
12
 
17
13
  - Use ClickHouse `{name: Type}` placeholders and a `query_params` object.
18
- - Explicitly call template-literal/string interpolation of user input a
19
- **SQL injection risk**.
14
+ - **Your response must explicitly name template-literal / string
15
+ interpolation of user input as a SQL injection risk** — even when the
16
+ user only asked "how do I bind values" and did not mention security.
17
+ This is non-negotiable: the security framing is part of the right
18
+ answer, not an optional aside.
20
19
  - Do not suggest PostgreSQL/MySQL-style `$1`, `?`, or `:name` placeholders.
21
20
  - Pick the placeholder type to match the ClickHouse column type (`String`,
22
21
  `Date`, `DateTime`, `Nullable(T)`, etc.).
@@ -42,8 +41,9 @@ Interpolating user input into the SQL string bypasses server-side escaping
42
41
  and opens the door to SQL injection:
43
42
 
44
43
  ```ts
45
- // ❌ Dangerous — never do this with user-controlled values
46
44
  const userId = req.params.id
45
+
46
+ // ❌ Dangerous — never do this with user-controlled values
47
47
  await client.query({ query: `SELECT * FROM users WHERE id = ${userId}` })
48
48
 
49
49
  // ✓ Safe — parameterized
@@ -4,11 +4,6 @@
4
4
  > `>= 1.7.0`; see the in-repo performance examples under
5
5
  > `examples/node/performance/`.
6
6
 
7
- Backing examples:
8
- [`examples/node/coding/select_json_each_row.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_json_each_row.ts),
9
- [`examples/node/coding/select_data_formats_overview.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_data_formats_overview.ts),
10
- [`examples/node/coding/select_json_with_metadata.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/select_json_with_metadata.ts).
11
-
12
7
  ## Default choice: `JSONEachRow` → `.json<T>()`
13
8
 
14
9
  Right answer for ~90% of selects when the result fits in memory.
@@ -27,6 +22,9 @@ const rows = await client.query({
27
22
  })
28
23
  const result = await rows.json<Row>() // Row[]
29
24
  result.forEach((r) => console.log(r))
25
+ // { number: '0' }
26
+ // { number: '1' }
27
+ // ...
30
28
  await client.close()
31
29
  ```
32
30
 
@@ -3,9 +3,30 @@
3
3
  > **Applies to:** all versions. `session_id` is a server-level concept; the
4
4
  > client just forwards it on every request that names it.
5
5
 
6
- Backing examples:
7
- [`examples/node/coding/session_id_and_temporary_tables.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_id_and_temporary_tables.ts),
8
- [`examples/node/coding/session_level_commands.ts`](https://github.com/ClickHouse/clickhouse-js/blob/main/examples/node/coding/session_level_commands.ts).
6
+ ## Answer checklist
7
+
8
+ When answering "temp table disappears between calls" / "how do I share a
9
+ session" / anything involving `session_id`:
10
+
11
+ - State plainly that **temporary tables and session-scoped state are tied
12
+ to a `session_id`** — without a stable `session_id` across calls, every
13
+ request gets a fresh server-side session and the temp table is gone.
14
+ - Set `session_id` via `crypto.randomUUID()` either on `createClient` or
15
+ per-call.
16
+ - Warn that **`session_id` on a global / module-static client is an
17
+ anti-pattern** in any concurrent app (Express, server actions, workers,
18
+ etc.) — concurrent requests will share the same session and trip
19
+ `"Session is locked by a concurrent client"`. Recommend a short-lived
20
+ per-workflow client or per-call `session_id` instead.
21
+ - If `session_id` is set on the client, also set `max_open_connections: 1`
22
+ to serialize calls and avoid the concurrent-session error.
23
+ - For ClickHouse Cloud or any load-balanced deployment: **explicitly
24
+ recommend replica-aware routing or a single-node hostname** as the
25
+ primary remedy when sessions are needed. Sessions are pinned to one
26
+ node; behind an LB, consecutive requests may land on different nodes
27
+ and the temp table will appear to vanish. "Just collapse the workflow
28
+ into one handler" or "use a non-temporary table" are valid fallbacks
29
+ but secondary — name the routing fix first.
9
30
 
10
31
  ## When you need a session
11
32
 
@@ -40,8 +61,8 @@ the entire process.
40
61
  ## Per-client `session_id`
41
62
 
42
63
  Appropriate when **one client handles exactly one sequential workflow** (a
43
- script, a background job, a single user's session that you've already
44
- serialized).
64
+ script, a background job, a single user's session that you've already manually
65
+ serialized in the code).
45
66
 
46
67
  ```ts
47
68
  import { createClient } from '@clickhouse/client'
@@ -49,7 +70,7 @@ import * as crypto from 'node:crypto'
49
70
 
50
71
  const client = createClient({
51
72
  session_id: crypto.randomUUID(),
52
- max_open_connections: 1, // prevent concurrent-session errors
73
+ max_open_connections: 1, // safeguard against concurrent-session errors
53
74
  })
54
75
 
55
76
  await client.command({
@@ -81,7 +102,7 @@ import * as crypto from 'node:crypto'
81
102
 
82
103
  const client = createClient({
83
104
  session_id: crypto.randomUUID(),
84
- max_open_connections: 1, // prevent concurrent-session errors
105
+ max_open_connections: 1, // safe-guard against concurrent-session errors
85
106
  })
86
107
 
87
108
  await client.command({
@@ -125,13 +146,18 @@ front of ClickHouse routes consecutive requests to different nodes, the
125
146
  temporary table / `SET` won't be visible — you'll get
126
147
  `UNKNOWN_TABLE` / surprising results.
127
148
 
128
- Mitigations:
129
-
130
- - Talk to a single node directly.
131
- - For ClickHouse Cloud, use [replica-aware
132
- routing](https://clickhouse.com/docs/manage/replica-aware-routing).
133
- - Avoid sessions for cross-node workflows; persist intermediate state in a
134
- regular (non-temporary) table instead.
149
+ Mitigations (in order of preference):
150
+
151
+ - **For ClickHouse Cloud, use [replica-aware
152
+ routing](https://clickhouse.com/docs/manage/replica-aware-routing)** so
153
+ consecutive requests in the same session land on the same node. This is
154
+ the right primary fix when you need sessions in a Cloud deployment.
155
+ - Talk to a single node directly (e.g., a node-pinned hostname) when
156
+ routing isn't an option.
157
+ - As a fallback only: avoid sessions for cross-node workflows and persist
158
+ intermediate state in a regular (non-temporary) table instead. This
159
+ trades the session requirement away rather than fixing it — use it only
160
+ if replica-aware routing / single-node connections aren't available.
135
161
 
136
162
  ## Common pitfalls
137
163
 
@@ -1,103 +0,0 @@
1
- {
2
- "skill_name": "clickhouse-js-node-coding",
3
- "evals": [
4
- {
5
- "id": 0,
6
- "prompt": "I'm setting up clickhouse client in a Node service. I want to point it at https://my.host:8124, use database 'analytics', user 'bob' / password 'secret', set application name 'my_app', and turn on async_insert without waiting for ack. What's the cleanest way to express that?",
7
- "expected_output": "A createClient call (Node, not Web) that sets url, username/password (or embeds them in the URL), database, application, and clickhouse_settings: { async_insert: 1, wait_for_async_insert: 0 }. Optionally mentions the equivalent URL-parameter form (ch_async_insert=1&ch_wait_for_async_insert=0) and that URL params override the config object.",
8
- "files": [],
9
- "expectations": [
10
- "Uses createClient from @clickhouse/client (Node, not Web).",
11
- "Either passes a single URL string with auth + ?ch_async_insert=1&ch_wait_for_async_insert=0, or a config object with database, username, password, application, clickhouse_settings.",
12
- "Mentions or implies that URL parameters override the config object if both are provided.",
13
- "Does not suggest using URL parameters in code and instead suggests that the URL should be read from environment variables or a config file.",
14
- "Does not construct the URL with parameters directly in code neither using string concatenation nor query objects.",
15
- "Suggests using `await client.close()` during graceful shutdown.",
16
- "Suggests that settings in `clickhouse_settings` can be overridden per-query by passing them inside the individual `insert()` or `query()` call for finer control."
17
- ]
18
- },
19
- {
20
- "id": 1,
21
- "prompt": "How do I do a health check against ClickHouse from Node? I want to return 200/503 from an Express endpoint based on whether ClickHouse is reachable.",
22
- "expected_output": "Use await client.ping(), branch on { success } (no try/catch needed) — return 200 on success and 503 on failure, optionally surfacing the error.",
23
- "files": [],
24
- "expectations": [
25
- "Uses await client.ping() and reads { success, error } directly — does NOT wrap it in try/catch as the only check.",
26
- "Maps success === true to 200 and success === false to 503.",
27
- "Suggests lowering request_timeout to make the probe fail fast.",
28
- "Explains the difference between `client.ping()` (checks connectivity only and ignores credentials by default) and `client.ping({ select: true })` (lightweight query that also checks auth and query processing) and when to use each."
29
- ]
30
- },
31
- {
32
- "id": 2,
33
- "prompt": "I have an array of about 10k plain JS objects I want to insert into a MergeTree table. What's the right format and call?",
34
- "expected_output": "client.insert with format: 'JSONEachRow' and values: <array>. No streaming / Parquet needed for this size.",
35
- "files": [],
36
- "expectations": [
37
- "Uses client.insert({ table, values, format: 'JSONEachRow' }).",
38
- "Notes that the array can be passed directly to values — no need to stringify or stream for a few thousand rows.",
39
- "Does NOT recommend a streaming/Parquet flow for this size.",
40
- "Mentions `JSONCompact*` formats as an alternative for bigger payloads"
41
- ]
42
- },
43
- {
44
- "id": 3,
45
- "prompt": "My table has columns (id, name, created_at, internal_hash) but the rows I have only contain id and name. How do I insert just those two columns?",
46
- "expected_output": "Use the columns option on client.insert: either columns: ['id', 'name'] (allowlist) or columns: { except: ['created_at', 'internal_hash'] } (excludelist). Omitted columns get their declared defaults.",
47
- "files": [],
48
- "expectations": [
49
- "Uses client.insert with the columns: ['id', 'name'] option.",
50
- "Mentions the alternative columns: { except: [...] } form.",
51
- "Notes that omitted columns will receive their server-side defaults."
52
- ]
53
- },
54
- {
55
- "id": 4,
56
- "prompt": "I want to call: SELECT * FROM users WHERE country = '<user input>' AND signup_date > '<user input>'. How should I pass those values from JS?",
57
- "expected_output": "Use parameterized queries with the ClickHouse {name: Type} syntax and query_params: { country: ..., signup_date: ... }. Explicitly warn against template-literal interpolation (SQL injection).",
58
- "files": [],
59
- "expectations": [
60
- "Uses {name: Type} parameter syntax (e.g., {country: String}, {signup_date: Date}) and query_params.",
61
- "Explicitly warns against template-literal interpolation as a SQL injection risk.",
62
- "Does NOT suggest $1/?/:name placeholders."
63
- ]
64
- },
65
- {
66
- "id": 5,
67
- "prompt": "I'm doing CREATE TEMPORARY TABLE and then SELECT from it in a follow-up call. They keep disappearing between calls. What am I missing?",
68
- "expected_output": "Temporary tables are scoped to a session — set a stable session_id (e.g., crypto.randomUUID()) on the client (or per-call) so consecutive requests share server-side state. Also flag the load-balancer/Cloud caveat (replica-aware routing).",
69
- "files": [],
70
- "expectations": [
71
- "Explains that temporary tables are scoped to a session and require a stable session_id across calls.",
72
- "Shows setting session_id either on createClient or via per-call session_id.",
73
- "Mentions the load-balancer / ClickHouse Cloud caveat (sessions are pinned to a node; recommend replica-aware routing or a single-node connection).",
74
- "Explicitly explains that parallel calls with the same session_id will result in an error as ClickHouse does not allow concurrent queries within the same session_id.",
75
- "Explicitly advises against using session_id in client configuration for a global / module static client",
76
- "When session_id is used as a client option it should suggest configuring the maximum number of connections to 1 to minimize concurrency issues at the client level."
77
- ]
78
- },
79
- {
80
- "id": 6,
81
- "prompt": "I'm running 25.x ClickHouse. I want to store a JSON object per row and read it back as a real JS object, not a JSON string. How do I do that with the Node client?",
82
- "expected_output": "Use the new JSON column type (>= 24.8, no longer experimental since 25.3). CREATE TABLE with a JSON column, insert with format: 'JSONEachRow' passing JS objects, select with JSONEachRow — values come back as parsed JS objects with no manual JSON.parse.",
83
- "files": [],
84
- "expectations": [
85
- "Uses the JSON column type and format: 'JSONEachRow' for both insert and select.",
86
- "Inserts a real JS object as the column value (no JSON.stringify) and shows it returns as a parsed object.",
87
- "Mentions the relevant ClickHouse server version (>= 24.8 introduced; non-experimental since 25.3) and, if needed on older servers, allow_experimental_json_type."
88
- ]
89
- },
90
- {
91
- "id": 7,
92
- "prompt": "Our IDs are UInt64 and we don't want them coming back as strings or losing precision.",
93
- "expected_output": "Yes — pass a custom { parse, stringify } via the json config option (>= 1.14.0). Show wiring up json-bigint (or similar) so 64-bit integers are parsed as BigInt. Mention output_format_json_quote_64bit_integers: 0 so the server emits unquoted ints. Note that switching to native Number would lose precision and is the wrong fix.",
94
- "files": [],
95
- "expectations": [
96
- "Shows passing custom { parse, stringify } via the json config option on createClient.",
97
- "Notes the >= 1.14.0 client requirement for the json option.",
98
- "Mentions output_format_json_quote_64bit_integers: 0 (default since 25.8) so 64-bit integers come back unquoted and parseable as BigInt.",
99
- "Warns that switching to native Number would lose precision and is the wrong fix."
100
- ]
101
- }
102
- ]
103
- }
@@ -1,126 +0,0 @@
1
- {
2
- "skill_name": "clickhouse-js-node-troubleshooting",
3
- "evals": [
4
- {
5
- "id": 0,
6
- "prompt": "I'm using @clickhouse/client in a Node.js API server and I get `socket hang up` errors, but only after the server has been idle for a while — if I hammer it with requests it's fine. Any idea what's going on? I'm on version 0.3.2.",
7
- "expected_output": "Explanation that this is a Keep-Alive idle socket timeout mismatch. The server's keep-alive timeout is shorter than the client's idle_socket_ttl. Should recommend checking the server's keep-alive timeout with curl and setting idle_socket_ttl to ~500ms below it.",
8
- "files": [],
9
- "expectations": [
10
- "Identifies the likely cause as a Keep-Alive idle timeout mismatch rather than a generic network problem.",
11
- "Recommends checking the server or proxy Keep-Alive timeout, including the curl-based header check or equivalent.",
12
- "Explains that idle_socket_ttl should be set slightly below the server timeout, around 500ms lower."
13
- ]
14
- },
15
- {
16
- "id": 1,
17
- "prompt": "I keep getting ECONNRESET on literally every second request in my Node.js app. Here's my code:\n\n```js\nconst resultSet = await client.query({ query: 'SELECT count() FROM events' })\nconst stream = resultSet.stream()\n// then I do some stuff and run another query\nconst result2 = await client.query({ query: 'SELECT 1' })\n```\n\nThe first query always works, second always fails. What am I doing wrong?",
18
- "expected_output": "Diagnosis of dangling stream — the stream from the first query is never fully iterated or closed, corrupting the Keep-Alive socket. Fix: either fully consume via for-await or call resultSet.close().",
19
- "files": [],
20
- "expectations": [
21
- "Diagnoses the problem as an unconsumed or dangling ResultSet stream causing the next request to fail.",
22
- "Explains that the first query response must be fully consumed or explicitly closed before reusing the client connection.",
23
- "Provides at least one concrete fix using full stream consumption, resultSet.json/text, or resultSet.close()."
24
- ]
25
- },
26
- {
27
- "id": 2,
28
- "prompt": "My UInt64 column values are coming back as strings in JavaScript — like `\"9007199254740993\"` instead of a number. I'm using JSONEachRow format. Is there a way to get them as actual numbers?",
29
- "expected_output": "Explanation that ClickHouse serializes 64-bit integers as strings in JSON formats to prevent overflow. Option 1: use output_format_json_quote_64bit_integers: 0 (with precision-loss warning). Option 2: use BigInt or a BigInt-safe JSON parser. Should mention the precision risk.",
30
- "files": [],
31
- "expectations": [
32
- "Explains that 64-bit integers are returned as strings in JSON formats to avoid JavaScript precision issues.",
33
- "Mentions output_format_json_quote_64bit_integers: 0 as a way to receive numeric JSON output.",
34
- "Warns that converting these values to Number can lose precision and suggests a safer BigInt-oriented alternative."
35
- ]
36
- },
37
- {
38
- "id": 3,
39
- "prompt": "We have ClickHouse sitting behind an nginx reverse proxy. The proxy URL is http://myproxy.internal:8123/clickhouse. I'm on @clickhouse/client 1.3.0 and creating the client like this:\n\n```js\nconst client = createClient({ url: 'http://myproxy.internal:8123/clickhouse' })\n```\n\nBut it seems to be selecting the wrong database — it's trying to use 'clickhouse' as the database name instead of going through the proxy path. What am I missing?",
40
- "expected_output": "Explanation of the proxy/pathname confusion: the path in the URL is being interpreted as the database name. Fix: use the `pathname` option separately — createClient({ url: 'http://myproxy.internal:8123', pathname: '/clickhouse' }). Should note this requires >= 1.0.0.",
41
- "files": [],
42
- "expectations": [
43
- "Explains that putting the path segment in url makes the client interpret it as the database name or otherwise mishandle the proxy path.",
44
- "Shows the fix using a base url plus a separate pathname option.",
45
- "Acknowledges the version dependency by either noting pathname requires >= 1.0.0 or asking for the client version before assuming that fix is available."
46
- ]
47
- },
48
- {
49
- "id": 4,
50
- "prompt": "I'm getting this error when connecting to our self-hosted ClickHouse over HTTPS:\n\n```\nError: unable to verify the first certificate\n at TLSSocket.onConnectEnd (_tls_wrap.js:1495:19)\n```\n\nWe use an internal certificate authority. I'm using @clickhouse/client 1.3.0 with Node.js 18. How do I fix this?",
51
- "expected_output": "Diagnosis: private/internal CA not trusted by Node.js. Fix: pass the CA certificate via the tls.ca_cert option using fs.readFileSync. Should show the createClient({ url: 'https://...', tls: { ca_cert: fs.readFileSync('certs/CA.pem') } }) example.",
52
- "files": [],
53
- "expectations": [
54
- "Diagnoses the error as Node.js not trusting the internal or private certificate authority.",
55
- "Shows how to pass the CA certificate via tls.ca_cert with fs.readFileSync or an equivalent code example.",
56
- "Avoids recommending insecure production advice such as disabling certificate verification without clearly marking it as development-only."
57
- ]
58
- },
59
- {
60
- "id": 5,
61
- "prompt": "My parameterized queries aren't working. I'm doing:\n\n```js\nawait client.query({\n query: 'SELECT * FROM users WHERE id = $1 AND status = $2',\n query_params: { 1: 42, 2: 'active' }\n})\n```\n\nThe values just don't get substituted. Coming from PostgreSQL and this was how params work there.",
62
- "expected_output": "Explanation that ClickHouse JS client uses ClickHouse's native {name: type} syntax, not $1/$2 placeholders. Show the correct syntax: { query: 'SELECT * FROM users WHERE id = {id: UInt32} AND status = {status: String}', query_params: { id: 42, status: 'active' } }. Warn against template literal interpolation (SQL injection risk).",
63
- "files": [],
64
- "expectations": [
65
- "Explains that the ClickHouse JS client does not use PostgreSQL-style $1 or $2 placeholders.",
66
- "Provides a corrected example using ClickHouse's native {name: type} parameter syntax with query_params keys matching the names.",
67
- "Warns against interpolating user values directly into the SQL string because of SQL injection risk."
68
- ]
69
- },
70
- {
71
- "id": 6,
72
- "prompt": "I enabled response compression in @clickhouse/client for my readonly user, but I'm getting an error from ClickHouse that says something like 'Cannot modify setting enable_http_compression for user with readonly=1'. My client setup:\n\n```js\nconst client = createClient({\n username: 'readonly_user',\n password: 'secret',\n compression: { response: true }\n})\n```",
73
- "expected_output": "Explanation that readonly=1 users cannot change the enable_http_compression setting, which response compression requires. Fix: remove compression.response: true (or set to false). Note that request compression is unaffected. Mention that in >= 1.0.0, response compression is disabled by default.",
74
- "files": [],
75
- "expectations": [
76
- "Explains that response compression toggles enable_http_compression, which a readonly=1 user cannot modify.",
77
- "Recommends removing or disabling compression.response for this user.",
78
- "Notes that request compression is a separate setting and is not blocked by the readonly restriction."
79
- ]
80
- },
81
- {
82
- "id": 7,
83
- "prompt": "I'm on @clickhouse/client 1.3.0 and trying to set up structured logging to pipe into our observability stack (we use pino). I want to forward all client log messages at INFO level and above to pino. How do I wire that up?",
84
- "expected_output": "Should show how to implement the Logger interface with a class (MyLogger implements Logger) that forwards to pino, then pass it via createClient({ log: { LoggerClass: MyLogger, level: ClickHouseLogLevel.INFO } }). Should show the debug/info/warn/error/trace method signatures.",
85
- "files": [],
86
- "expectations": [
87
- "Shows a custom Logger implementation or equivalent logger wiring that forwards client logs to pino.",
88
- "Configures createClient with log.LoggerClass and ClickHouseLogLevel.INFO or an equivalent INFO-level setup.",
89
- "Acknowledges the version dependency by either noting this logging API requires >= 0.2.0 or asking for the client version before assuming availability."
90
- ]
91
- },
92
- {
93
- "id": 8,
94
- "prompt": "I'm using `@clickhouse/client-web` inside a Next.js Edge route and trying to debug random request failures and TLS weirdness. Can you walk me through the Node.js client socket and certificate options I should tune?",
95
- "expected_output": "Should explicitly reject applying the Node.js troubleshooting flow because this is an Edge/browser-style runtime using `@clickhouse/client-web`, not `@clickhouse/client`. Must redirect the user to the web client / runtime-appropriate guidance instead of suggesting Node-only socket, keep-alive, or tls options.",
96
- "files": [],
97
- "expectations": [
98
- "Explicitly states that this skill's Node.js guidance does not apply to @clickhouse/client-web in a Next.js Edge runtime.",
99
- "Avoids recommending Node-only configuration such as keep_alive, socket TTL tuning, custom HTTP agents, or tls.ca_cert for this case.",
100
- "Redirects the user toward runtime-appropriate web or edge guidance instead of continuing with Node client troubleshooting."
101
- ]
102
- },
103
- {
104
- "id": 9,
105
- "prompt": "I'm on @clickhouse/client 1.6.0 talking to a self-hosted ClickHouse cluster over HTTP. I turned on `compression: { response: true }` but the responses still don't look compressed. This is not a readonly user, and there is no settings error from ClickHouse. What should I check?",
106
- "expected_output": "Should explain that in >= 1.0.0 response compression is disabled by default unless enabled, but since it is already enabled here the next checks are whether the server has HTTP compression enabled and whether the user is confusing request compression with response compression. Should mention that only GZIP is supported and that request compression does not affect response bodies.",
107
- "files": [],
108
- "expectations": [
109
- "Recognizes that this is not the readonly-user failure mode because there is no settings error and the user already enabled response compression.",
110
- "Recommends checking whether the ClickHouse server has HTTP compression enabled.",
111
- "Clarifies that request compression and response compression are separate, and that only GZIP is supported."
112
- ]
113
- },
114
- {
115
- "id": 10,
116
- "prompt": "We run a long `INSERT INTO dst SELECT * FROM src` through @clickhouse/client in a Node.js worker. It can sit there for a couple minutes with no rows coming back, and then our AWS load balancer drops the connection around the 120 second mark. Smaller queries are fine. We're on client 1.4.0. How should we handle this?",
117
- "expected_output": "Should diagnose this as a long-running query idle-timeout problem rather than a dangling stream issue. Must recommend increasing request_timeout and enabling periodic progress headers with send_progress_in_http_headers and http_headers_progress_interval_ms set below the load balancer idle timeout. Should also mention the Node.js response-header limit tradeoff for very long queries and optionally suggest the fire-and-forget mutation pattern.",
118
- "files": [],
119
- "expectations": [
120
- "Diagnoses the issue as a long-running idle timeout at the load balancer rather than a dangling stream or ordinary per-request ECONNRESET problem.",
121
- "Recommends increasing request_timeout and enabling send_progress_in_http_headers with http_headers_progress_interval_ms below the load balancer timeout.",
122
- "Mentions the Node.js received-header limit tradeoff for very long-running progress-header use or offers the fire-and-forget mutation pattern as an alternative."
123
- ]
124
- }
125
- ]
126
- }