@makebelieve21213-packages/clickhouse-client
v1.0.1
Published
ClickHouse database client module for NestJS with migration support for OLAP analytics
Maintainers
Readme
@makebelieve21213-packages/clickhouse-client
ClickHouse database client для NestJS с поддержкой TypeScript и полной типобезопасностью.
📋 Содержание
- Возможности
- Требования
- Установка
- Структура пакета
- Быстрый старт
- API Reference
- Примеры использования
- Миграции
- Troubleshooting
- Тестирование
🚀 Возможности
- ✅ NestJS интеграция - глобальный модуль с forRootAsync для простой интеграции
- ✅ Type-safe API - полная типобезопасность TypeScript с экспортируемыми типами
- ✅ ClickHouse клиент - использование официального @clickhouse/client
- ✅ Конфигурация - поддержка конфигурации через ConfigModule
- ✅ Миграции - поддержка миграций через clickhouse-migrations
- ✅ Singleton паттерн - эффективное использование ресурсов
- ✅ Graceful shutdown - корректное закрытие соединения при завершении приложения
- ✅ Обработка ошибок - детальная обработка ошибок API с логированием
- ✅ Параметризованные запросы - безопасность через параметризацию
- ✅ 100% покрытие тестами - надежность и качество кода
📋 Требования
- Node.js: >= 22.11.0
- NestJS: >= 11.0.0
- ClickHouse Server: рекомендуется версия 23.x или выше
📦 Установка
npm install @makebelieve21213-packages/clickhouse-clientЗависимости
{
"@nestjs/common": "^11.0.0",
"@nestjs/config": "^4.0.0",
"@clickhouse/client": "^1.12.0",
"@packages/logger": "^1.0.0",
"@packages/types": "^1.0.0",
"reflect-metadata": "^0.1.13 || ^0.2.0"
}📁 Структура пакета
src/
├── main/ # NestJS модуль
├── types/ # TypeScript типы
├── migrations/ # SQL миграции
├── utils/ # Утилиты
└── index.ts # Экспорты🏗️ Архитектура
Пакет предоставляет NestJS глобальный модуль ClickhouseClientModule для работы с ClickHouse базой данных через официальный клиент @clickhouse/client.
Основные компоненты:
ClickhouseClientModule- NestJS глобальный модульClickhouseClientService- сервис для работы с базой данныхClickhouseConfig- конфигурация клиента
🔧 Быстрый старт
Шаг 1: Настройка переменных окружения
CH_URL=http://localhost:8123
CH_USERNAME=default
CH_PASSWORD=your-password-here
CH_DATABASE=NA_KOLENKE_CHAINШаг 2: Создание конфигурации
Создайте файл clickhouse.config.ts в вашем сервисе:
import { registerAs } from "@nestjs/config";
import type { ClickhouseConfig } from "@makebelieve21213-packages/clickhouse-client";
import { CONFIG_SYMBOLS } from "@packages/types";
const clickhouseConfig = registerAs<ClickhouseConfig>(
CONFIG_SYMBOLS.CLICKHOUSE,
(): ClickhouseConfig => ({
url: process.env.CH_URL!,
username: process.env.CH_USERNAME!,
password: process.env.CH_PASSWORD!,
database: process.env.CH_DATABASE!,
}),
);
export default clickhouseConfig;Шаг 3: Регистрация модуля
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ClickhouseClientModule, type ClickhouseConfig } from '@makebelieve21213-packages/clickhouse-client';
import { CONFIG_SYMBOLS } from '@packages/types';
import clickhouseConfig from 'src/configs/clickhouse.config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [clickhouseConfig],
}),
ClickhouseClientModule.forRootAsync({
imports: [ConfigModule],
useFactory: (...args: unknown[]) => {
const configService = args[0] as ConfigService;
return configService.get<ClickhouseConfig>(CONFIG_SYMBOLS.CLICKHOUSE)!;
},
inject: [ConfigService],
}),
],
})
export class AppModule {}Шаг 4: Использование сервиса
// analytics.service.ts
import { Injectable } from '@nestjs/common';
import { ClickhouseClientService } from '@makebelieve21213-packages/clickhouse-client';
import type { Transfer } from './types';
@Injectable()
export class AnalyticsService {
constructor(private readonly clickhouse: ClickhouseClientService) {}
async getTransfers(blockNumber: number): Promise<Transfer[]> {
return await this.clickhouse.query<Transfer>(
'SELECT * FROM transfers WHERE block_number > {blockNumber:UInt64}',
{ blockNumber }
);
}
}📚 API Reference
ClickhouseClientModule
forRootAsync(options):
ClickhouseClientModule.forRootAsync({
imports: [ConfigModule],
useFactory: (...args: unknown[]) => {
const configService = args[0] as ConfigService;
return configService.get<ClickhouseConfig>(CONFIG_SYMBOLS.CLICKHOUSE)!;
},
inject: [ConfigService],
})Экспортирует: ClickhouseClientService (глобально)
ClickhouseClientService
Конфигурация:
url: string- URL сервера ClickHouseusername: string- имя пользователяpassword: string- парольdatabase: string- имя базы данных
Методы:
exec(sql, params?)
Выполняет SQL команды (CREATE, INSERT, UPDATE, DELETE, ALTER). Поддерживает выполнение нескольких команд, разделенных точкой с запятой.
async exec(sql: string, params?: Record<string, unknown>): Promise<void>Пример:
// Создание таблицы
await clickhouse.exec(`
CREATE TABLE IF NOT EXISTS test_table (
id UInt64,
name String,
created_at DateTime64(3, 'UTC')
) ENGINE = MergeTree() ORDER BY id
`);
// Выполнение с параметрами
await clickhouse.exec(
'ALTER TABLE test_table DELETE WHERE id = {id:UInt64}',
{ id: 123 }
);
// Множественные команды
await clickhouse.exec(`
CREATE TABLE IF NOT EXISTS table1 (id UInt64) ENGINE = Memory;
CREATE TABLE IF NOT EXISTS table2 (id UInt64) ENGINE = Memory;
`);query<T>(sql, params?)
Выполняет SELECT запросы и возвращает результат в формате JSON.
async query<T>(sql: string, params?: Record<string, unknown>): Promise<T[]>Пример:
interface Transfer {
contract_address: string;
block_number: number;
tx_hash: string;
from: string;
to: string;
value: number;
}
// Простой запрос
const transfers = await clickhouse.query<Transfer>(
'SELECT * FROM transfers LIMIT 10'
);
// Запрос с параметрами
const recentTransfers = await clickhouse.query<Transfer>(
'SELECT * FROM transfers WHERE block_number > {blockNumber:UInt64} AND timestamp > {date:DateTime64}',
{
blockNumber: 18000000,
date: new Date().toISOString()
}
);
// Агрегация данных
const stats = await clickhouse.query<{ count: number; total_value: number }>(
'SELECT count() as count, sum(value) as total_value FROM transfers WHERE block_number > {blockNumber:UInt64}',
{ blockNumber: 18000000 }
);insert<T>(tableName, rows)
Вставляет массив объектов в указанную таблицу. Автоматически фильтрует null и undefined значения.
async insert<T>(tableName: string, rows: T[]): Promise<void>Пример:
const transfersData = [
{
contract_address: '0x123...',
transfer_type: 'ERC-20',
block_number: 18000001,
timestamp: new Date().toISOString(),
tx_hash: '0xabc...',
from: '0x456...',
to: '0x789...',
value: 100.5,
symbol: 'ETH',
gas_used: 21000,
gas_price: 20000000000,
fee_eth: 0.00042
},
// ... больше данных
];
// Вставка в основную таблицу
await clickhouse.insert('transfers', transfersData);
// Вставка в буферную таблицу (рекомендуется для массовых операций)
await clickhouse.insert('transfers_buffer', transfersData);get client()
Прямой доступ к клиенту ClickHouse для расширенных операций.
get client(): ClickHouseClient | undefinedПример:
// Получение статистики сервера
const stats = await clickhouse.client?.query({
query: 'SELECT * FROM system.metrics',
format: 'JSON'
});🧪 Примеры использования
Выполнение команд
await clickhouse.exec('CREATE TABLE test (id UInt64) ENGINE = Memory');Типизированные запросы
const transfers = await clickhouse.query<Transfer>(
'SELECT * FROM transfers WHERE block_number > {blockNumber:UInt64}',
{ blockNumber: 18000000 }
);Массовая вставка
await clickhouse.insert('transfers_buffer', transfersData);📝 Миграции
Пакет включает систему миграций для управления схемой базы данных через clickhouse-migrations.
Структура миграций
Миграции находятся в папке src/migrations/:
1_create_main_database.sql- Создание основной базы данныхNA_KOLENKE_CHAIN2_create_analytics_table.sql- Создание таблицtransfersиtransfers_buffer
Выполнение миграций
pnpm run migrate:devСоздание новой миграции
Создайте новый файл в
src/migrations/с номером и описательным именем:3_add_user_table.sqlДобавьте SQL команды:
CREATE TABLE IF NOT EXISTS NA_KOLENKE_CHAIN.users ( id UInt64, wallet_address String, created_at DateTime64(3, 'UTC') ) ENGINE = MergeTree() ORDER BY id;Выполните миграцию:
pnpm run migrate:dev
🚨 Troubleshooting
Connection timeout
Решение: Проверить доступность сервера ClickHouse, проверить переменные окружения CH_URL, CH_USERNAME, CH_PASSWORD.
Table not found
Решение: Выполнить миграции через pnpm run migrate:dev, проверить правильность имени базы данных.
Query execution failed
Решение: Проверить синтаксис SQL запроса, проверить типы параметров в параметризованных запросах.
Insert failed
Решение: Проверить соответствие полей объектов названиям колонок таблицы, проверить типы данных.
🧪 Тестирование
Пакет имеет 100% покрытие тестами.
pnpm test # Все тесты
pnpm test:coverage # С покрытием
pnpm test:watch # Watch режим🔧 Конфигурация
interface ClickhouseConfig {
url: string; // URL сервера ClickHouse
username: string; // Имя пользователя
password: string; // Пароль
database: string; // Имя базы данных
}Примечание: Конфигурация должна создаваться в сервисе, который использует пакет.
📦 Зависимости
@nestjs/common- NestJS core@nestjs/config- NestJS config@clickhouse/client- официальный клиент ClickHouse@packages/logger- логирование@packages/types- типы для конфигурацииclickhouse-migrations- утилита для миграцийreflect-metadata- TypeScript decorators
📄 Лицензия
MIT
👥 Автор
Skryabin Aleksey
