Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

ClickHouse JS

Le JS client officiel pour se connecter à ClickHouse. Le client est écrit en TypeScript et fournit le typage de l'API publique du client.

Il ne comporte aucune dépendance, est optimisé pour des performances maximales et a été testé avec différentes versions et configurations de ClickHouse (single node on-premise, cluster on-premise et ClickHouse Cloud).

Deux versions distinctes du client sont disponibles pour différents environnements :

  • @clickhouse/client - Node.js uniquement
  • @clickhouse/client-web - navigateurs (Chrome/Firefox), Cloudflare workers

Si vous utilisez TypeScript, assurez-vous d'employer au minimum la version 4.5, qui active la syntaxe d'import et d'export intégrée.

Le code source du client est disponible dans le repository GitHub ClickHouse-JS.

Prérequis de l’environnement (node.js)

Node.js doit être disponible dans l’environnement pour exécuter le client. Le client est compatible avec toutes les versions maintenues de Node.js.

Lorsqu’une version de Node.js approche de sa fin de vie, le client cesse de la prendre en charge, car elle est considérée comme obsolète et non sécurisée.

Prise en charge des versions actuelles de Node.js :

Version de Node.js Prise en charge ?
24.x
22.x
20.x
18.x Best effort

Configuration requise de l'environnement (web)

La version web du client est officiellement testée avec les dernières versions de Chrome et Firefox, et peut être utilisée comme dépendance, par exemple dans des applications React/Vue/Angular ou dans Cloudflare workers.

Installation

Pour installer la dernière version stable du client Node.js, exécutez :

npm i @clickhouse/client

Installation de la version Web :

npm i @clickhouse/client-web

Compatibilité avec ClickHouse

Version du client ClickHouse
1.12.0 24.8+

Le client fonctionnera probablement aussi avec des versions antérieures ; toutefois, cette compatibilité est fournie au mieux et n'est pas garantie. Si vous utilisez une version de ClickHouse antérieure à 23.3, veuillez consulter la politique de sécurité de ClickHouse et envisager une mise à niveau.

Exemples

Nous cherchons à couvrir différents scénarios d’utilisation du client à l’aide des examples dans le dépôt du client.

Une vue d’ensemble est disponible dans le README des examples.

Si certains points restent flous ou ne figurent pas dans les examples ou dans la documentation suivante, n’hésitez pas à nous contacter.

API du client

La plupart des exemples sont compatibles aussi bien avec Node.js qu’avec la version web du client, sauf mention contraire explicite.

Création d’une instance de client

Vous pouvez créer autant d’instances de client que nécessaire à l’aide de la fabrique createClient :

import { createClient } from '@clickhouse/client' // or '@clickhouse/client-web'

const client = createClient({
  /* configuration */
})

Si votre environnement ne prend pas en charge les modules ESM, vous pouvez utiliser la syntaxe CJS à la place :

const { createClient } = require('@clickhouse/client');

const client = createClient({
  /* configuration */
})

Une instance du client peut être préconfigurée au moment de son instanciation.

Configuration

Lors de la création d’une instance de client, les paramètres de connexion suivants peuvent être ajustés :

Paramètre Description Valeur par défaut Voir aussi
url?: string URL d’une instance ClickHouse. http://localhost:8123 Documentation de configuration de l’URL
pathname?: string Chemin facultatif à ajouter à l’URL ClickHouse après analyse par le client. '' Documentation sur le proxy avec un pathname
request_timeout?: number Délai d’expiration de la requête, en millisecondes. 30_000 -
compression?: { **response**?: boolean; **request**?: boolean } Active la compression. - Documentation sur la compression
username?: string Nom de l’utilisateur pour le compte duquel les requêtes sont effectuées. default -
password?: string Mot de passe de l’utilisateur. '' -
application?: string Nom de l’application utilisant le client Node.js. clickhouse-js -
database?: string Nom de la base de données à utiliser. default -
clickhouse_settings?: ClickHouseSettings Paramètres ClickHouse à appliquer à toutes les requêtes. {} -
log?: { **LoggerClass**?: Logger, **level**?: ClickHouseLogLevel } Configuration des logs internes du client. - Documentation sur la journalisation
session_id?: string ID de session ClickHouse facultatif à envoyer avec chaque requête. - -
keep_alive?: { **enabled**?: boolean } Activé par défaut dans les versions Node.js et Web. - -
http_headers?: Record<string, string> En-têtes HTTP supplémentaires pour les requêtes ClickHouse envoyées. - Documentation sur le reverse proxy avec authentification
roles?: string string[] Noms des rôles ClickHouse à associer aux requêtes envoyées. -

Paramètres de configuration spécifiques à Node.js

Paramètre Description Valeur par défaut Voir aussi
max_open_connections?: number Nombre maximal de sockets ouvertes autorisées par hôte. 10 -
tls?: { **ca_cert**: Buffer, **cert**?: Buffer, **key**?: Buffer } Configure les certificats TLS. - Documentation TLS
keep_alive?: { **enabled**?: boolean, **idle_socket_ttl**?: number } - - Documentation Keep Alive
http_agent?: http.Agent https.Agent
Expérimental
Agent HTTP personnalisé du client. -
set_basic_auth_header?: boolean
Expérimental
Définit l’en-tête Authorization avec les identifiants d’authentification Basic. true utilisation de ce paramètre dans la documentation de l’agent HTTP

Configuration de l’URL

Il est possible de configurer la plupart des paramètres de l’instance client à l’aide d’une URL. Le format de l’URL est http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]. Dans presque tous les cas, le nom d’un paramètre donné reflète son chemin dans l’interface des options de configuration, à quelques exceptions près. Les paramètres suivants sont pris en charge :

Paramètre Type
pathname une chaîne arbitraire.
application_id une chaîne arbitraire.
session_id une chaîne arbitraire.
request_timeout nombre non négatif.
max_open_connections nombre non négatif, supérieur à zéro.
compression_request booléen. Voir ci-dessous (1)
compression_response booléen.
log_level valeurs autorisées : OFF, TRACE, DEBUG, INFO, WARN, ERROR.
keep_alive_enabled booléen.
clickhouse_setting_* or ch_* voir ci-dessous (2)
http_header_* voir ci-dessous (3)
(Node.js uniquement) keep_alive_idle_socket_ttl nombre non négatif.
  • (1) Pour les booléens, les valeurs valides sont true/1 et false/0.
  • (2) Tout paramètre préfixé par clickhouse_setting_ ou ch_ verra ce préfixe supprimé, et le reste sera ajouté à clickhouse_settings du client. Par exemple, ?ch_async_insert=1&ch_wait_for_async_insert=1 sera identique à :
createClient({
  clickhouse_settings: {
    async_insert: 1,
    wait_for_async_insert: 1,
  },
})

Remarque : les valeurs booléennes de clickhouse_settings doivent être transmises sous la forme 1/0 dans l’URL.

  • (3) Comme pour (2), mais pour la configuration http_header. Par exemple, ?http_header_x-clickhouse-auth=foobar est l’équivalent de :
createClient({
  http_headers: {
    'x-clickhouse-auth': 'foobar',
  },
})

Connexion

Récupérez vos informations de connexion

Pour vous connecter à ClickHouse via HTTP(S), vous avez besoin des informations suivantes :

Paramètre(s) Description
HOST and PORT En général, le port est 8443 lors de l’utilisation de TLS, ou 8123 sans TLS.
DATABASE NAME Par défaut, une base de données nommée default est disponible ; utilisez le nom de la base de données à laquelle vous voulez vous connecter.
USERNAME and PASSWORD Par défaut, le nom d’utilisateur est default. Utilisez le nom d’utilisateur adapté à votre cas d’usage.

Les informations de votre service ClickHouse Cloud sont disponibles dans la console ClickHouse Cloud. Sélectionnez un service, puis cliquez sur Connect :

Bouton Connect du service ClickHouse Cloud

Choisissez HTTPS. Les détails de connexion s’affichent dans un exemple de commande curl.

Détails de connexion HTTPS ClickHouse Cloud

Si vous utilisez ClickHouse autogéré, les détails de connexion sont définis par votre administrateur ClickHouse.

Aperçu de la connexion

Le client se connecte via le protocole HTTP ou HTTPS. La prise en charge de RowBinary est en bonne voie, voir l’issue correspondante.

L’exemple suivant montre comment configurer une connexion à ClickHouse Cloud. Il suppose que les valeurs url (y compris le protocole et le port) et password sont définies via des variables d’environnement, et que l’utilisateur default est utilisé.

Exemple : création d’une instance du client Node.js à l’aide de variables d’environnement pour la configuration.

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 ?? '',
})

Le dépôt client contient plusieurs exemples qui utilisent des variables d’environnement, comme la création d’une table dans ClickHouse Cloud, l’utilisation des inserts asynchrones, et bien d’autres.

Pool de connexions (Node.js uniquement)

Pour éviter le surcoût lié à l'établissement d'une connexion à chaque requête, le client crée un pool de connexions vers ClickHouse afin de les réutiliser, en s'appuyant sur un mécanisme Keep-Alive. Par défaut, Keep-Alive est activé et la taille du pool de connexions est définie sur 10, mais vous pouvez la modifier avec l'option de configuration max_open_connections.

Rien ne garantit que la même connexion du pool sera utilisée pour les requêtes suivantes, sauf si l'utilisateur définit max_open_connections: 1. Cela est rarement nécessaire, mais peut être requis lorsque les utilisateurs emploient des tables temporaires.

Voir aussi : Configuration de Keep-Alive.

ID de requête

Chaque méthode qui envoie une requête ou une instruction SQL (command, exec, insert, select) fournit query_id dans le résultat. Cet identifiant unique est attribué par le client à chaque requête et peut être utile pour récupérer des données depuis system.query_log, s'il est activé dans la configuration du serveur, ou pour annuler des requêtes longues (voir l'exemple). Si nécessaire, query_id peut être redéfini par l'utilisateur dans les paramètres des méthodes command/query/exec/insert.

Base parameters for all client methods

Plusieurs paramètres peuvent être appliqués à toutes les méthodes du client (query/command/insert/exec).

interface BaseQueryParams {
  // ClickHouse settings that can be applied on query level.
  clickhouse_settings?: ClickHouseSettings
  // Parameters for query binding.
  query_params?: Record<string, unknown>
  // AbortSignal instance to cancel a query in progress.
  abort_signal?: AbortSignal
  // query_id override; if not specified, a random identifier will be generated automatically.
  query_id?: string
  // session_id override; if not specified, the session id will be taken from the client configuration.
  session_id?: string
  // credentials override; if not specified, the client's credentials will be used.
  auth?: { username: string, password: string }
  // A specific list of roles to use for this query. Overrides the roles set in the client configuration.
  role?: string | Array<string>
}

Méthode query

Elle est utilisée pour la plupart des statements pouvant renvoyer une réponse, comme SELECT, ou pour envoyer des DDLs comme CREATE TABLE, et doit donc être attendue. Le jeu de résultats renvoyé est destiné à être traité dans l'application.

interface QueryParams extends BaseQueryParams {
  // Query to execute that might return some data.
  query: string
  // Format of the resulting dataset. Default: JSON.
  format?: DataFormat
}

interface ClickHouseClient {
  query(params: QueryParams): Promise<ResultSet>
}

Voir aussi : Paramètres de base pour toutes les méthodes du client.

Abstractions de jeu de résultats et de lignes

ResultSet fournit plusieurs méthodes pratiques pour traiter les données dans votre application.

L’implémentation Node.js de ResultSet utilise Stream.Readable en interne, tandis que la version web utilise l’API Web ReadableStream.

Vous pouvez consommer le ResultSet en appelant les méthodes text ou json sur ResultSet et charger en mémoire l’ensemble des lignes renvoyées par la requête.

Vous devriez commencer à consommer le ResultSet dès que possible, car il maintient le flux de réponse ouvert et garde par conséquent la connexion sous-jacente occupée. Le client ne met pas les données entrantes en mémoire tampon afin d’éviter une utilisation mémoire potentiellement excessive par l’application.

Sinon, si l’ensemble est trop volumineux pour tenir en mémoire en une seule fois, vous pouvez appeler la méthode stream et traiter les données en mode streaming. Chacun des chunks de la réponse sera alors transformé en tableaux de lignes relativement petits (la taille de ce tableau dépend de la taille du chunk que le client reçoit du server, qui peut varier, ainsi que de la taille de chaque ligne), un chunk à la fois.

Veuillez consulter la liste des formats de données pris en charge afin de déterminer le format le mieux adapté au streaming dans votre cas. Par exemple, si vous souhaitez diffuser des objets JSON, vous pouvez choisir JSONEachRow, et chaque ligne sera analysée comme un objet JS, ou éventuellement le format plus compact JSONCompactColumns, où chaque ligne sera représentée par un tableau compact de valeurs. Voir aussi : fichiers en streaming.

interface BaseResultSet<Stream> {
  // See "Query ID" section above
  query_id: string

  // Consume the entire stream and get the contents as a string
  // Can be used with any DataFormat
  // Should be called only once
  text(): Promise<string>

  // Consume the entire stream and parse the contents as a JS object
  // Can be used only with JSON formats
  // Should be called only once
  json<T>(): Promise<T>

  // Returns a readable stream for responses that can be streamed
  // Every iteration over the stream provides an array of Row[] in the selected DataFormat
  // Should be called only once
  stream(): Stream
}

interface Row {
  // Get the content of the row as a plain string
  text: string

  // Parse the content of the row as a JS object
  json<T>(): T
}

Exemple : (Node.js/Web) Une requête dont le jeu de résultats est au format JSONEachRow, qui consomme l’intégralité du flux et analyse le contenu en objets JS. Code source.

const resultSet = await client.query({
  query: 'SELECT * FROM my_table',
  format: 'JSONEachRow',
})
const dataset = await resultSet.json() // or `row.text` to avoid parsing JSON

Exemple : (Node.js uniquement) Résultat de la requête en streaming au format JSONEachRow avec l’approche classique on('data'). Cela est équivalent à la syntaxe for await const. Code source.

const rows = await client.query({
  query: 'SELECT number FROM system.numbers_mt LIMIT 5',
  format: 'JSONEachRow', // or JSONCompactEachRow, JSONStringsEachRow, etc.
})
const stream = rows.stream()
stream.on('data', (rows: Row[]) => {
  rows.forEach((row: Row) => {
    console.log(row.json()) // or `row.text` to avoid parsing JSON
  })
})
await new Promise((resolve, reject) => {
  stream.on('end', () => {
    console.log('Completed!')
    resolve(0)
  })
  stream.on('error', reject)
})

Exemple : (Node.js uniquement) Résultat de requête en streaming au format CSV avec l’approche classique on('data'). Cette approche est interchangeable avec la syntaxe for await const. Code source

const resultSet = await client.query({
  query: 'SELECT number FROM system.numbers_mt LIMIT 5',
  format: 'CSV', // or TabSeparated, CustomSeparated, etc.
})
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)
})

Exemple : (Node.js uniquement) Résultat de requête en streaming sous forme d’objets JS au format JSONEachRow, consommé avec la syntaxe for await const. Cette méthode est interchangeable avec l’approche classique on('data'). Code source.

const resultSet = await client.query({
  query: 'SELECT number FROM system.numbers LIMIT 10',
  format: 'JSONEachRow', // or JSONCompactEachRow, JSONStringsEachRow, etc.
})
for await (const rows of resultSet.stream()) {
  rows.forEach(row => {
    console.log(row.json())
  })
}

Exemple : (Web uniquement) Itération sur le ReadableStream d’objets.

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())
  })
}

Méthode insert

C’est la méthode principale pour insérer des données.

export interface InsertResult {
  query_id: string
  executed: boolean
}

interface ClickHouseClient {
  insert(params: InsertParams): Promise<InsertResult>
}

Le type de retour est minimal, car nous ne nous attendons pas à ce que le serveur renvoie des données et nous consommons immédiatement le flux de réponse.

Si un tableau vide est fourni à la méthode insert, l'instruction insert ne sera pas envoyée au serveur ; à la place, la méthode se résoudra immédiatement avec { query_id: '...', executed: false }. Si query_id n'a pas été fourni dans les params de la méthode dans ce cas, il prendra la valeur d'une chaîne vide dans le résultat, car renvoyer un UUID aléatoire généré par le client pourrait prêter à confusion, puisque la query avec ce query_id n'existera pas dans la table system.query_log.

Si l'instruction insert a été envoyée au serveur, l'indicateur executed sera true.

Méthode insert et streaming dans Node.js

Elle peut fonctionner soit avec un Stream.Readable, soit avec un simple Array<T>, selon le format de données spécifié pour la méthode insert. Voir aussi cette section sur le streaming de fichiers.

La méthode insert doit normalement être utilisée avec await ; toutefois, il est possible de spécifier un flux d'entrée et d'attendre l'opération insert plus tard, uniquement une fois le flux terminé (ce qui entraînera également la résolution de la promesse insert). Cela peut être utile pour des écouteurs d'événements et des scénarios similaires, mais la gestion des erreurs peut être complexe, avec de nombreux cas limites côté client. Envisagez plutôt d'utiliser les insertions asynchrones, comme illustré dans cet exemple.

interface InsertParams<T> extends BaseQueryParams {
  // Table name to insert the data into
  table: string
  // A dataset to insert.
  values: ReadonlyArray<T> | Stream.Readable
  // Format of the dataset to insert.
  format?: DataFormat
  // Allows to specify which columns the data will be inserted into.
  // - An array such as `['a', 'b']` will generate: `INSERT INTO table (a, b) FORMAT DataFormat`
  // - An object such as `{ except: ['a', 'b'] }` will generate: `INSERT INTO table (* EXCEPT (a, b)) FORMAT DataFormat`
  // By default, the data is inserted into all columns of the table,
  // and the generated statement will be: `INSERT INTO table FORMAT DataFormat`.
  columns?: NonEmptyArray<string> | { except: NonEmptyArray<string> }
}

Voir aussi : Base parameters for all client methods.

Exemple : (Node.js/Web) Insérer un tableau de valeurs. Code source.

await client.insert({
  table: 'my_table',
  // structure should match the desired format, JSONEachRow in this example
  values: [
    { id: 42, name: 'foo' },
    { id: 42, name: 'bar' },
  ],
  format: 'JSONEachRow',
})

Exemple : (Node.js uniquement) Insérez les données d’un fichier CSV en streaming. Code source. Voir aussi : streaming de fichiers.

await client.insert({
  table: 'my_table',
  values: fs.createReadStream('./path/to/a/file.csv'),
  format: 'CSV',
})

Exemple : excluez certaines colonnes de l’instruction INSERT.

Soit une définition de table telle que :

CREATE OR REPLACE TABLE mytable
(id UInt32, message String)
ENGINE MergeTree()
ORDER BY (id)

Insérez uniquement une colonne précise :

// Generated statement: INSERT INTO mytable (message) FORMAT JSONEachRow
await client.insert({
  table: 'mytable',
  values: [{ message: 'foo' }],
  format: 'JSONEachRow',
  // `id` column value for this row will be zero (default for UInt32)
  columns: ['message'],
})

Excluez certaines colonnes :

// Generated statement: INSERT INTO mytable (* EXCEPT (message)) FORMAT JSONEachRow
await client.insert({
  table: tableName,
  values: [{ id: 144 }],
  format: 'JSONEachRow',
  // `message` column value for this row will be an empty string
  columns: {
    except: ['message'],
  },
})

Consultez le code source pour plus de détails.

Exemple : insérez dans une base de données différente de celle fournie à l'instance client. Code source.

await client.insert({
  table: 'mydb.mytable', // Fully qualified name including the database
  values: [{ id: 42, message: 'foo' }],
  format: 'JSONEachRow',
})

Limitations de la version web

Actuellement, les insertions dans @clickhouse/client-web ne fonctionnent qu’avec les formats Array<T> et JSON*. L’insertion de flux n’est pas encore prise en charge dans la version web en raison de la compatibilité limitée des navigateurs.

Par conséquent, l’interface InsertParams de la version web diffère légèrement de celle de Node.js, car values est limité au seul type ReadonlyArray<T> :

interface InsertParams<T> extends BaseQueryParams {
  // Table name to insert the data into
  table: string
  // A dataset to insert.
  values: ReadonlyArray<T>
  // Format of the dataset to insert.
  format?: DataFormat
  // Allows to specify which columns the data will be inserted into.
  // - An array such as `['a', 'b']` will generate: `INSERT INTO table (a, b) FORMAT DataFormat`
  // - An object such as `{ except: ['a', 'b'] }` will generate: `INSERT INTO table (* EXCEPT (a, b)) FORMAT DataFormat`
  // By default, the data is inserted into all columns of the table,
  // and the generated statement will be: `INSERT INTO table FORMAT DataFormat`.
  columns?: NonEmptyArray<string> | { except: NonEmptyArray<string> }
}

Cela pourra changer à l’avenir. Voir aussi : Base parameters for all client methods.

Méthode command

Elle peut être utilisée pour des instructions SQL qui n'ont aucune sortie, lorsque la clause FORMAT ne s'applique pas, ou lorsque la réponse ne vous intéresse pas du tout. CREATE TABLE ou ALTER TABLE sont des exemples de telles instructions.

Doit être utilisée avec await.

Le flux de réponse est détruit immédiatement, ce qui signifie que le socket sous-jacent est libéré.

interface CommandParams extends BaseQueryParams {
  // Statement to execute.
  query: string
}

interface CommandResult {
  query_id: string
}

interface ClickHouseClient {
  command(params: CommandParams): Promise<CommandResult>
}

Voir aussi : Base parameters for all client methods.

Exemple : (Node.js/Web) Créer une table dans ClickHouse Cloud. Code source.

await client.command({
  query: `
    CREATE TABLE IF NOT EXISTS my_cloud_table
    (id UInt64, name String)
    ORDER BY (id)
  `,
  // Recommended for cluster usage to avoid situations where a query processing error occurred after the response code,
  // and HTTP headers were already sent to the client.
  // See https://clickhouse.com/docs/interfaces/http/#response-buffering
  clickhouse_settings: {
    wait_end_of_query: 1,
  },
})

Exemple : (Node.js/Web) Créer une table sur une instance ClickHouse auto-hébergée. Code source.

await client.command({
  query: `
    CREATE TABLE IF NOT EXISTS my_table
    (id UInt64, name String)
    ENGINE MergeTree()
    ORDER BY (id)
  `,
})

Exemple : (Node.js/Web) INSERT FROM SELECT

await client.command({
  query: `INSERT INTO my_table SELECT '42'`,
})

Méthode Exec

Si vous avez une requête personnalisée qui ne correspond pas à query/insert et que son résultat vous intéresse, vous pouvez utiliser exec comme alternative à command.

exec renvoie un flux lisible qui DOIT être consommé ou détruit par l’application.

interface ExecParams extends BaseQueryParams {
  // Statement to execute.
  query: string
}

interface ClickHouseClient {
  exec(params: ExecParams): Promise<QueryResult>
}

Voir aussi : Base parameters for all client methods.

Le type de retour du stream diffère entre les versions Node.js et Web.

Node.js :

export interface QueryResult {
  stream: Stream.Readable
  query_id: string
}

Web :

export interface QueryResult {
  stream: ReadableStream
  query_id: string
}

Ping

La méthode ping, fournie pour vérifier l’état de la connectivité, renvoie true si le serveur est accessible.

Si le serveur est inaccessible, l’erreur sous-jacente est également incluse dans le résultat.

type PingResult =
  | { success: true }
  | { success: false; error: Error }

/** Parameters for the health-check request - using the built-in `/ping` endpoint.
 *  This is the default behavior for the Node.js version. */
export type PingParamsWithEndpoint = {
  select: false
  /** AbortSignal instance to cancel a request in progress. */
  abort_signal?: AbortSignal
  /** Additional HTTP headers to attach to this particular request. */
  http_headers?: Record<string, string>
}
/** Parameters for the health-check request - using a SELECT query.
 *  This is the default behavior for the Web version, as the `/ping` endpoint does not support CORS.
 *  Most of the standard `query` method params, e.g., `query_id`, `abort_signal`, `http_headers`, etc. will work,
 *  except for `query_params`, which does not make sense to allow in this method. */
export type PingParamsWithSelectQuery = { select: true } & Omit<
  BaseQueryParams,
  'query_params'
>
export type PingParams = PingParamsWithEndpoint | PingParamsWithSelectQuery

interface ClickHouseClient {
  ping(params?: PingParams): Promise<PingResult>
}

Ping peut être utile pour vérifier si le serveur est disponible au démarrage de l’application, en particulier avec ClickHouse Cloud, où une instance peut être en veille et se réveiller après un ping : dans ce cas, il peut être utile de réessayer plusieurs fois en laissant un délai entre chaque tentative.

Notez que, par défaut, la version Node.js utilise l’endpoint /ping, tandis que la version Web utilise une simple requête SELECT 1 pour obtenir un résultat similaire, car l’endpoint /ping ne prend pas en charge CORS.

Exemple : (Node.js/Web) Un simple ping vers une instance du serveur ClickHouse. NB : pour la version Web, les erreurs capturées seront différentes. Code source.

const result = await client.ping();
if (!result.success) {
  // process result.error
}

Exemple : Si vous souhaitez aussi vérifier les informations d’authentification lors de l’appel de la méthode ping, ou préciser des paramètres supplémentaires tels que query_id, vous pouvez l’utiliser comme suit :

const result = await client.ping({ select: true, /* query_id, abort_signal, http_headers, or any other query params */ });

La méthode ping accepte la plupart des paramètres standard de la méthode query — voir la définition de type PingParamsWithSelectQuery.

Close (Node.js uniquement)

Ferme toutes les connexions ouvertes et libère les ressources. Sans effet dans la version web.

await client.close()

Streaming de fichiers (Node.js uniquement)

Il existe plusieurs exemples de streaming de fichiers avec des formats de données courants (NDJSON, CSV, Parquet) dans le dépôt du client.

Le streaming d'autres formats vers un fichier devrait être similaire à celui de Parquet, la seule différence réside dans le format utilisé pour l'appel à query (JSONEachRow, CSV, etc.) et dans le nom du fichier de sortie.

Formats de données pris en charge

Le client gère les formats de données en JSON ou en texte.

Si vous spécifiez format comme l’un des formats de la famille JSON (JSONEachRow, JSONCompactEachRow, etc.), le client sérialisera et désérialisera les données pendant la communication sur le wire.

Les données fournies dans les formats texte « bruts » (familles CSV, TabSeparated et CustomSeparated) sont envoyées sur le wire sans transformation supplémentaire.

Format Entrée (tableau) Entrée (objet) Entrée/Sortie (flux) Sortie (JSON) Sortie (texte)
JSON ✔️ ✔️ ✔️
JSONCompact ✔️ ✔️ ✔️
JSONObjectEachRow ✔️ ✔️ ✔️
JSONColumnsWithMetadata ✔️ ✔️ ✔️
JSONStrings ❌️ ✔️ ✔️
JSONCompactStrings ✔️ ✔️
JSONEachRow ✔️ ✔️ ✔️ ✔️
JSONEachRowWithProgress ❌️ ✔️ ❗- voir ci-dessous ✔️ ✔️
JSONStringsEachRow ✔️ ✔️ ✔️ ✔️
JSONCompactEachRow ✔️ ✔️ ✔️ ✔️
JSONCompactStringsEachRow ✔️ ✔️ ✔️ ✔️
JSONCompactEachRowWithNames ✔️ ✔️ ✔️ ✔️
JSONCompactEachRowWithNamesAndTypes ✔️ ✔️ ✔️ ✔️
JSONCompactStringsEachRowWithNames ✔️ ✔️ ✔️ ✔️
JSONCompactStringsEachRowWithNamesAndTypes ✔️ ✔️ ✔️ ✔️
CSV ✔️ ✔️
CSVWithNames ✔️ ✔️
CSVWithNamesAndTypes ✔️ ✔️
TabSeparated ✔️ ✔️
TabSeparatedRaw ✔️ ✔️
TabSeparatedWithNames ✔️ ✔️
TabSeparatedWithNamesAndTypes ✔️ ✔️
CustomSeparated ✔️ ✔️
CustomSeparatedWithNames ✔️ ✔️
CustomSeparatedWithNamesAndTypes ✔️ ✔️
Parquet ✔️ ✔️❗- voir ci-dessous

Pour Parquet, le principal cas d'utilisation de SELECT sera probablement l'écriture du flux résultant dans un fichier. Voir l'exemple dans le dépôt du client.

JSONEachRowWithProgress est un format uniquement en sortie qui prend en charge les informations de progression dans le flux. Voir cet exemple pour plus de détails.

La liste complète des formats d'entrée et de sortie de ClickHouse est disponible ici.

Types de données ClickHouse pris en charge

Type Statut type JS
UInt8/16/32 ✔️ number
UInt64/128/256 ✔️ ❗- voir ci-dessous string
Int8/16/32 ✔️ number
Int64/128/256 ✔️ ❗- voir ci-dessous string
Float32/64 ✔️ number
Decimal ✔️ ❗- voir ci-dessous number
Boolean ✔️ boolean
String ✔️ string
FixedString ✔️ string
UUID ✔️ string
Date32/64 ✔️ string
DateTime32/64 ✔️ ❗- voir ci-dessous string
Enum ✔️ string
LowCardinality ✔️ string
Array(T) ✔️ T[]
(nouveau) JSON ✔️ object
Variant(T1, T2…) ✔️ T (dépend du variant)
Dynamic ✔️ T (dépend du variant)
Nested ✔️ T[]
Tuple(T1, T2, …) ✔️ [T1, T2, …]
Tuple(n1 T1, n2 T2…) ✔️ { n1: T1; n2: T2; …}
Nullable(T) ✔️ type JS pour T ou 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

La liste complète des formats ClickHouse pris en charge est disponible ici.

Voir aussi :

Points à noter concernant les types Date/Date32

Comme le client insère les valeurs sans conversion de type supplémentaire, les colonnes de type Date/Date32 ne peuvent être insérées que sous forme de chaînes de caractères.

Exemple : Insérer une valeur de type Date. Code source

await client.insert({
  table: 'my_table',
  values: [ { date: '2022-09-05' } ],
  format: 'JSONEachRow',
})

Cependant, si vous utilisez des colonnes DateTime ou DateTime64, vous pouvez utiliser à la fois des chaînes et des objets Date JavaScript. Les objets Date JavaScript peuvent être transmis à insert tels quels, avec date_time_input_format défini sur best_effort. Consultez cet example pour plus de détails.

Points à connaître concernant les types Decimal*

Il est possible d'insérer des valeurs Decimal à l'aide des formats de la famille JSON*. Supposons que nous ayons une table définie comme suit :

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)

Nous pouvons insérer des valeurs sans perte de précision en utilisant leur représentation sous forme de chaîne :

await client.insert({
  table: 'my_table',
  values: [{
    id: 1,
    dec32:  '1234567.89',
    dec64:  '123456789123456.789',
    dec128: '1234567891234567891234567891.1234567891',
    dec256: '12345678912345678912345678911234567891234567891234567891.12345678911234567891',
  }],
  format: 'JSONEachRow',
})

Cependant, lors de l’interrogation de données aux formats JSON*, ClickHouse renvoie par défaut les Decimals sous forme de nombres, ce qui peut entraîner une perte de précision. Pour éviter cela, vous pouvez convertir les Decimals en chaîne de caractères dans la requête :

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',
})

Voir cet exemple pour en savoir plus.

Types entiers : Int64, Int128, Int256, UInt64, UInt128, UInt256

Bien que le serveur puisse l'accepter comme un nombre, il est renvoyé sous forme de chaîne dans les formats de sortie de la famille JSON* afin d'éviter un dépassement de capacité d'entier, car les valeurs maximales de ces types dépassent Number.MAX_SAFE_INTEGER.

Ce comportement peut toutefois être modifié avec le paramètre output_format_json_quote_64bit_integers .

Exemple : Ajustez le format de sortie JSON pour les nombres 64 bits.

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 } ])

Paramètres ClickHouse

Le client peut modifier le comportement de ClickHouse via le mécanisme de paramètres. Les paramètres peuvent être définis au niveau de l’instance cliente afin d’être appliqués à chaque requête envoyée à ClickHouse :

const client = createClient({
  clickhouse_settings: {}
})

Ou un paramètre peut être configuré au niveau de la requête :

client.query({
  clickhouse_settings: {}
})

Un fichier de déclaration de types répertoriant tous les paramètres ClickHouse pris en charge est disponible ici.

Rubriques avancées

Requêtes avec paramètres

Vous pouvez créer une requête avec des paramètres et leur transmettre des valeurs depuis l’application cliente. Cela évite d’avoir à formater la requête avec des valeurs dynamiques spécifiques côté client.

Formatez une requête comme d’habitude, puis placez entre accolades les valeurs que vous souhaitez transmettre à la requête depuis les paramètres de l’application, au format suivant :

{<name>: <data_type>}

où :

  • name — Identifiant de l’espace réservé.
  • data_type - Type de données de la valeur du paramètre d’application.

Exemple :: Requête avec paramètres. Code source .

await client.query({
  query: 'SELECT plus({val1: Int32}, {val2: Int32})',
  format: 'CSV',
  query_params: {
    val1: 10,
    val2: 20,
  },
})

Consultez https://clickhouse.com/docs/interfaces/client#cli-queries-with-parameters-syntax pour plus d’informations.

Compression

NB : la compression des requêtes n’est actuellement pas disponible dans la version Web. La compression des réponses fonctionne normalement. La version Node.js prend en charge les deux.

Les applications de données qui traitent de grands ensembles de données en transit peuvent bénéficier de l’activation de la compression. Actuellement, seul GZIP est pris en charge via zlib.

createClient({
  compression: {
    response: true,
    request: true
  }
})

Les paramètres de configuration sont :

  • response: true indique à ClickHouse server de renvoyer un corps de réponse compressé. Valeur par défaut : response: false
  • request: true active la compression du corps de la requête client. Valeur par défaut : request: false

Journalisation (Node.js uniquement)

L’implémentation par défaut du logger envoie les enregistrements de journal vers stdout via les méthodes console.debug/info, et vers stderr via les méthodes console.warn/error. Vous pouvez personnaliser la logique de journalisation en fournissant un LoggerClass, et choisir le niveau de journalisation souhaité via le paramètre level (la valeur par défaut est WARN) :

import type { Logger } from '@clickhouse/client'

// All three LogParams types are exported by the client
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,
  }
})

Actuellement, le client consigne les événements suivants :

  • TRACE - informations de bas niveau sur le cycle de vie des sockets Keep-Alive
  • DEBUG - informations de réponse (sans les en-têtes d’autorisation ni les informations sur l’hôte)
  • INFO - niveau principalement inutilisé ; affiche le niveau de journalisation actuel lors de l’initialisation du client
  • WARN - erreurs non fatales ; une requête ping ayant échoué est consignée comme avertissement, car l’erreur sous-jacente est incluse dans le résultat renvoyé
  • ERROR - erreurs fatales des méthodes query/insert/exec/command, par exemple en cas d’échec d’une requête

Vous trouverez l’implémentation par défaut du Logger ici.

Certificats TLS (Node.js uniquement)

Le client Node.js prend également en charge, de manière optionnelle, le TLS de base (autorité de certification uniquement) et le TLS mutuel (autorité de certification et certificats client).

Exemple de configuration TLS de base, en supposant que vos certificats se trouvent dans le dossier certs et que le fichier de l’autorité de certification s’appelle CA.pem :

const client = createClient({
  url: 'https://<hostname>:<port>',
  username: '<username>',
  password: '<password>', // if required
  tls: {
    ca_cert: fs.readFileSync('certs/CA.pem'),
  },
})

Exemple de configuration du TLS mutuel à l’aide de certificats clients :

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`),
  },
})

Consultez des exemples complets de TLS simple et mutuel dans le dépôt.

Configuration de Keep-Alive (Node.js uniquement)

Le client active Keep-Alive par défaut dans l'agent HTTP sous-jacent, ce qui signifie que les sockets ouvertes seront réutilisées pour les requêtes suivantes et que l'en-tête Connection: keep-alive sera envoyé. Les sockets inactives restent dans le pool de connexions pendant 2 500 millisecondes par défaut (voir les notes sur l'ajustement de cette option).

keep_alive.idle_socket_ttl doit avoir une valeur sensiblement inférieure à celle de la configuration du serveur/LB. La raison principale est qu'avec HTTP/1.1, le serveur peut fermer les sockets sans en avertir le client ; si le serveur ou le load balancer ferme la connexion avant le client, celui-ci peut tenter de réutiliser une socket fermée, ce qui entraîne une erreur socket hang up.

Si vous modifiez keep_alive.idle_socket_ttl, gardez à l'esprit qu'il doit toujours être aligné sur la configuration Keep-Alive de votre serveur/LB, tout en restant toujours inférieur à celle-ci, afin de garantir que le serveur ne ferme jamais la connexion ouverte en premier.

Ajuster idle_socket_ttl

Le client définit keep_alive.idle_socket_ttl sur 2 500 millisecondes, car cela peut être considéré comme la valeur par défaut la plus sûre ; côté serveur, keep_alive_timeout peut être défini jusqu’à seulement 3 secondes dans les versions de ClickHouse antérieures à 23.11 sans modification de config.xml.

Vous pouvez trouver la valeur correcte du délai d’expiration Keep-Alive dans les en-têtes de réponse du serveur en exécutant la commande suivante :

curl -is --data-binary "SELECT 1" <clickhouse_url>

Vérifiez les valeurs des en-têtes Connection et Keep-Alive dans la réponse. Par exemple :

Connection: Keep-Alive
Keep-Alive: timeout=10

Dans ce cas, keep_alive_timeout est de 10 secondes, et vous pouvez essayer d’augmenter keep_alive.idle_socket_ttl à 9000, voire 9500 millisecondes, afin de garder les sockets inactifs ouverts un peu plus longtemps que la valeur par défaut. Surveillez d’éventuelles erreurs "Socket hang-up", qui indiqueront que le serveur ferme les connexions avant que le client ne le fasse, puis réduisez la valeur jusqu’à ce que les erreurs disparaissent.

Dépannage

Si vous rencontrez des erreurs socket hang up même avec la dernière version du client, plusieurs options s’offrent à vous pour résoudre ce problème :

  • Activez la journalisation avec au moins le niveau WARN (par défaut). Cela permet de vérifier s’il existe un flux non consommé ou abandonné dans le code de l’application : la couche de transport l’enregistrera au niveau WARN, car cela pourrait entraîner la fermeture du socket par le serveur. Vous pouvez activer la journalisation dans la configuration du client comme suit :

    const client = createClient({
      log: { level: ClickHouseLogLevel.WARN },
    })
  • Assurez-vous que la configuration souhaitée est appliquée à la bonne instance de client. Si vous avez plusieurs instances de client dans votre application, vérifiez bien que celle que vous utilisez pour les requêtes a la valeur correcte pour keep_alive.idle_socket_ttl.

  • Réduisez le paramètre keep_alive.idle_socket_ttl de 500 millisecondes dans la configuration du client. Dans certaines situations, par exemple en cas de latence réseau élevée entre le client et le serveur, cela peut être utile afin d’écarter le cas où une requête sortante récupérerait un socket que le serveur s’apprête à fermer.

  • Si cette erreur se produit pendant des requêtes longues sans aucune donnée entrante ou sortante (par exemple un INSERT FROM SELECT de longue durée), cela peut être dû à un load balancer ou à d’autres composants réseau qui ferment les connexions longues ou les requêtes longues. Vous pouvez essayer de forcer l’arrivée de données pendant les requêtes longues en utilisant une combinaison de ces paramètres ClickHouse :

    const client = createClient({
      // Ici, nous supposons que certaines requêtes auront un temps d’exécution supérieur à 5 minutes
      request_timeout: 400_000,
      /** Ces paramètres combinés permettent d’éviter les problèmes de dépassement de délai du LB dans le cas de requêtes longues sans données entrantes ou sortantes,
       *  comme `INSERT FROM SELECT` et autres similaires, car la connexion pourrait être marquée comme inactive par le LB et fermée brutalement.
       *  Dans ce cas, nous supposons que le LB a un délai d’inactivité de connexion de 120s, donc nous définissons 110s comme valeur « sûre ». */
      clickhouse_settings: {
        send_progress_in_http_headers: 1,
        http_headers_progress_interval_ms: '110000', // UInt64, doit être transmis sous forme de chaîne
      },
    })

    Gardez toutefois à l’esprit que la taille totale des en-têtes reçus est limitée à 16KB dans les versions récentes de Node.js ; après un certain nombre d’en-têtes de progression reçus, autour de 70 à 80 dans nos tests, une exception sera générée.

    Il est également possible d’utiliser une approche complètement différente, en évitant totalement le temps d’attente sur le réseau ; cela peut se faire en tirant parti de la « fonctionnalité » de l’interface HTTP selon laquelle les mutations ne sont pas annulées lorsque la connexion est perdue. Voir cet exemple (partie 2) pour plus de détails.

  • La fonctionnalité Keep-Alive peut être entièrement désactivée. Dans ce cas, le client ajoutera également l’en-tête Connection: close à chaque requête, et l’agent HTTP sous-jacent ne réutilisera pas les connexions. Le paramètre keep_alive.idle_socket_ttl sera ignoré, puisqu’il n’y aura pas de sockets inactifs. Cela entraînera une surcharge supplémentaire, puisqu’une nouvelle connexion sera établie pour chaque requête.

    const client = createClient({
      keep_alive: {
        enabled: false,
      },
    })
  • Écartez les problèmes potentiels liés au reste de la pile réseau, y compris Node.js lui-même, en exécutant un simple test en ligne de commande avec la même instance ClickHouse et le même chemin réseau (c’est-à-dire depuis la même machine ou le même segment réseau, par exemple un pod Kubernetes), par exemple avec curl :

    curl -is --user '<user>:<password>' --data-binary "SELECT 1" <clickhouse_url>

    Vous pouvez l’exécuter en boucle pendant plusieurs minutes. Si vous voyez des erreurs similaires dans curl, il est probable que le problème ne soit pas lié à la configuration du client, mais plutôt à la pile réseau ou à la configuration du serveur.

  • Pour tester la connexion avec les fonctionnalités natives de Node.js, vous pouvez essayer de créer une simple requête HTTP vers le serveur ClickHouse en utilisant l’API fetch intégrée :

  const response = await fetch('<clickhouse_url>?query=SELECT+1', {
    method: 'POST',
    headers: {
      'Authorization': 'Basic ' + Buffer.from('<user>:<password>').toString('base64'),
    }
  })
  • Dans certains cas, le code applicatif ou les adaptateurs du framework peuvent ajouter un ping() préventif avant l’exécution réelle de la requête, ce qui peut conduire à une situation où la requête ping() réussit, mais où la requête suivante échoue avec une erreur "socket hang up" en raison du même problème sous-jacent de connexions inactives. Si vous observez ce schéma dans les journaux, vérifiez s’il existe une option permettant de désactiver les pings préventifs dans votre framework ou votre code applicatif. Cela devrait également aider à réduire le risque d’être limité en débit par l’un des composants réseau intermédiaires.

  • Assurez-vous que l’application elle-même dispose de suffisamment de temps CPU et que le réseau n’est pas bridé par le fournisseur d’hébergement. Différents moyens de surveillance, comme les métriques de pause du GC, les métriques de latence de la boucle d’événements et d’autres du même type, peuvent également être utiles pour écarter d’éventuels problèmes de manque de ressources.

  • Essayez de vérifier votre code applicatif avec la règle ESLint no-floating-promises activée, ce qui aidera à identifier les promesses non gérées susceptibles d’entraîner des flux et des sockets orphelins.

Utilisateurs en lecture seule

Lorsque vous utilisez le client avec un utilisateur readonly=1, la compression de la réponse ne peut pas être activée, car elle nécessite le paramètre enable_http_compression. La configuration suivante entraînera une erreur :

const client = createClient({
  compression: {
    response: true, // won't work with a readonly=1 user
  },
})

Consultez l’exemple, qui détaille davantage les limitations des utilisateurs avec readonly=1.

Proxy avec un chemin d’accès

Si votre instance ClickHouse se trouve derrière un proxy et que l’URL contient un chemin d’accès, comme par exemple http://proxy:8123/clickhouse&#95;server, indiquez clickhouse_server comme option de configuration pathname (avec ou sans slash initial) ; sinon, s’il est fourni directement dans l’url, il sera interprété comme l’option database. Plusieurs segments sont pris en charge, par ex. /my_proxy/db.

const client = createClient({
  url: 'http://proxy:8123',
  pathname: '/clickhouse_server',
})

Proxy inverse avec authentification

Si vous avez un proxy inverse avec authentification en amont de votre déploiement ClickHouse, vous pouvez utiliser le paramètre http_headers pour fournir les en-têtes nécessaires :

const client = createClient({
  http_headers: {
    'My-Auth-Header': '...',
  },
})

Agent HTTP/HTTPS personnalisé (expérimental, Node.js uniquement)

Par défaut, le client configure l'agent HTTP ou HTTPS sous-jacent à l'aide des paramètres fournis dans la configuration du client (comme max_open_connections, keep_alive.enabled, tls), qui gérera les connexions au serveur ClickHouse. De plus, si des certificats TLS sont utilisés, l'agent sous-jacent sera configuré avec les certificats nécessaires, et les en-têtes d'authentification TLS appropriés seront appliqués.

À partir de la version 1.2.0, il est possible de fournir au client un agent HTTP ou HTTPS personnalisé, en remplacement de l'agent sous-jacent par défaut. Cela peut être utile dans le cas de configurations réseau complexes. Les conditions suivantes s'appliquent si un agent personnalisé est fourni :

  • Les options max_open_connections et tls n'auront aucun effet et seront ignorées par le client, car elles font partie de la configuration de l'agent sous-jacent.
  • keep_alive.enabled réglera uniquement la valeur par défaut de l'en-tête Connection (true -> Connection: keep-alive, false -> Connection: close).
  • Bien que la gestion des sockets keep-alive inactifs continue de fonctionner (car elle n'est pas liée à l'agent, mais au socket lui-même), il est désormais possible de la désactiver entièrement en définissant keep_alive.idle_socket_ttl sur 0.

Exemples d’utilisation d’un agent personnalisé

Utilisation d’un agent HTTP ou HTTPS personnalisé sans certificat :

const agent = new http.Agent({ // or https.Agent
  keepAlive: true,
  keepAliveMsecs: 2500,
  maxSockets: 10,
  maxFreeSockets: 10,
})
const client = createClient({
  http_agent: agent,
})

Utilisation d’un agent HTTPS personnalisé avec un TLS de base et un certificat d’AC :

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,
  // With a custom HTTPS agent, the client won't use the default HTTPS connection implementation; the headers should be provided manually
  http_headers: {
    'X-ClickHouse-User': 'username',
    'X-ClickHouse-Key': 'password',
  },
  // Important: authorization header conflicts with the TLS headers; disable it.
  set_basic_auth_header: false,
})

Utilisation d’un agent HTTPS personnalisé avec TLS mutuel :

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,
  // With a custom HTTPS agent, the client won't use the default HTTPS connection implementation; the headers should be provided manually
  http_headers: {
    'X-ClickHouse-User': 'username',
    'X-ClickHouse-Key': 'password',
    'X-ClickHouse-SSL-Certificate-Auth': 'on',
  },
  // Important: authorization header conflicts with the TLS headers; disable it.
  set_basic_auth_header: false,
})

Avec des certificats et un agent HTTPS personnalisé, il est probablement nécessaire de désactiver l’en-tête Authorization par défaut via le paramètre set_basic_auth_header (introduit dans la version 1.2.0), car il entre en conflit avec les en-têtes TLS. Tous les en-têtes TLS doivent être fournis manuellement.

Limites connues (Node.js/web)

  • Il n'existe pas de mappage de données pour les ensembles de résultats ; seules les primitives du langage sont donc utilisées. La prise en charge de certains mappages de types de données est prévue avec le support du format RowBinary.
  • Il existe quelques points d'attention concernant les types de données Decimal* et Date* / DateTime*.
  • Lors de l'utilisation des formats de la famille JSON*, les nombres supérieurs à Int32 sont représentés sous forme de chaînes, car les valeurs maximales des types Int64+ dépassent Number.MAX_SAFE_INTEGER. Voir la section types entiers pour plus de détails.

Limitations connues (web)

  • Le streaming des requêtes SELECT fonctionne, mais il est désactivé pour les insertions (au niveau des types également).
  • La compression des requêtes est désactivée et la configuration est ignorée. La compression des réponses fonctionne.
  • La journalisation n’est pas encore prise en charge.

Conseils d'optimisation des performances

  • Pour réduire la consommation de mémoire de l'application, envisagez d'utiliser des streams pour les inserts volumineux (par ex. depuis des fichiers) et les requêtes SELECT lorsque c'est pertinent. Pour les écouteurs d'événements et les cas d'usage similaires, les async inserts peuvent être une autre bonne option, car elles permettent de minimiser, voire d'éviter complètement, le batching côté client. Des exemples d'async insert sont disponibles dans le dépôt du client, avec async_insert_ comme préfixe de nom de fichier.
  • Le client n'active pas la compression des requêtes ni des réponses par défaut. Cependant, lors de la lecture ou de l'insertion de grands jeux de données, vous pouvez envisager de l'activer via ClickHouseClientConfigOptions.compression (uniquement pour request ou response, ou pour les deux).
  • La compression a un coût important en termes de performances. L'activer pour request ou response ralentira respectivement les SELECT ou les inserts, mais réduira le volume de trafic réseau transféré par l'application.

Contactez-nous

Si vous avez des questions ou besoin d'aide, n'hésitez pas à nous contacter sur le Slack de la communauté (canal #clickhouse-js) ou via les issues GitHub.

Navigation