ClickHouse has become the go-to choice for high-performance analytics, powering everything from real-time dashboards to complex data warehouses. As TypeScript continues to dominate the JavaScript ecosystem, combining these two technologies creates a powerful foundation for modern data applications. In this guide, we'll get you from zero to running your first ClickHouse query in TypeScript in under 10 minutes.
For type-safe ClickHouse queries, check out hypequery, the TypeScript SDK for Clickhouse
Why ClickHouse + TypeScript?
Before diving into the implementation, let's understand why this combination is so compelling:
ClickHouse's Strengths:
- Blazing Fast Analytics: Designed for OLAP workloads, ClickHouse can process billions of rows in seconds
- Columnar Storage: Optimised for analytical queries with incredible compression ratios
- SQL Compatibility: Familiar SQL syntax with powerful analytical functions
- Horizontal Scaling: Easily handles petabyte-scale datasets
TypeScript's Benefits:
- Type Safety: Catch errors at compile time rather than runtime
- Developer Experience: Superior IDE support with autocomplete and refactoring
- Maintainability: Self-documenting code with clear interfaces
- Modern Tooling: Excellent ecosystem for testing, building, and deploying
Together, they provide a robust foundation for building analytics applications that are both performant and maintainable.
Quick Setup with @clickhouse/client
Let's start with the basics. First, install the official ClickHouse client:
npm install @clickhouse/client
Here's a minimal setup to connect to ClickHouse:
import { createClient } from '@clickhouse/client'
const client = createClient({
host: 'http://localhost:8123',
username: 'default',
password: '',
database: 'default'
})
// Test the connection
async function testConnection() {
try {
const result = await client.query({
query: 'SELECT version()',
format: 'JSON'
})
console.log('Connected to ClickHouse:', await result.json())
} catch (error) {
console.error('Connection failed:', error)
}
}
testConnection()
Creating Your First Table & Query
async function createTable() {
await client.command(`
CREATE TABLE IF NOT EXISTS user_events (
id UUID DEFAULT generateUUIDv4(),
user_id UInt64,
event_type String,
timestamp DateTime DEFAULT now(),
properties Map(String, String)
) ENGINE = MergeTree()
ORDER BY (user_id, timestamp)
`)
}
async function insertSampleData() {
await client.insert({
table: 'user_events',
values: [
{ user_id: 1, event_type: 'page_view', properties: { page: '/home' } },
{ user_id: 1, event_type: 'click', properties: { button: 'signup' } },
{ user_id: 2, event_type: 'page_view', properties: { page: '/pricing' } }
],
format: 'JSONEachRow'
})
}
async function queryEvents() {
const result = await client.query({
query: `
SELECT
event_type,
count() as event_count
FROM user_events
WHERE timestamp >= now() - INTERVAL 1 DAY
GROUP BY event_type
ORDER BY event_count DESC
`,
format: 'JSON'
})
const data = await result.json()
console.log('Event counts:', data)
}
The Type Safety Gap
While the above code works, you'll quickly notice some significant limitations:
- No Query Type Safety: The SQL strings are just strings – no autocomplete, no validation.
- Unknown Result Types: TypeScript doesn't know what structure your queries return.
- Manual Type Definitions: You need to manually define interfaces for every query result
- Runtime Errors: Typos in column names or table names only surface at runtime
Here's a typical issue:
// This compiles but fails at runtime
const result = await client.query({
query: 'SELECT non_existent_column FROM user_events', // Typo!
format: 'JSON'
})
// TypeScript doesn't know what this contains
const data = await result.json() // data is 'any'
Introducing Type Safety with hypequery
This is where hypequery comes in. hypequery is a TypeScript SDK specifically designed for building type-safe dashboards and analytics applications with ClickHouse.
It solves the type safety problems we just identified while maintaining the full power of ClickHouse.
Setting Up hypequery
npm install @hypequery/clickhouse @clickhouse/client
Generate your database schema
npx hypequery-generate-types --host=http://localhost:8123 --username=default --password=password --database=my_db
This creates a generated-schema.ts
file that you can import in your application:
Type-Safe Querying
Now you can write fully type-safe queries!
import { createQueryBuilder } from '@hypequery/clickhouse'
import { IntrospectedSchema } from './generated-schema'
const hq = createQueryBuilder<IntrospectedSchema>({
host: 'http://localhost:8123',
password: 'your-password',
username: 'your-username',
database: 'default'
})
// Fully type-safe query with autocomplete
const eventCounts = await hq
.table('user_events')
.select(['event_type'])
.count('id', 'event_count')
.where('timestamp', 'gte', 'now() - INTERVAL 1 DAY')
.groupBy('event_type')
.orderBy('event_count', 'DESC')
.execute()
// eventCounts is fully typed - TypeScript knows the exact structure
eventCounts.forEach(row => {
console.log(`${row.event_type}: ${row.event_count}`)
})
Features
hypequery provides several advanced features that make building analytics applications easier including:
- Table joins and filtering which can be defined once and applied across your queries or at a query level
- Streaming support for memory-efficient processing of large result sets
- Support for ClickHouse specific functions, common table expressions and raw SQL for those hard to reach edge cases
- First class developer experience with comprehensive query logging, debugging and SQL generation
Why This Matters
The combination of ClickHouse's performance and TypeScript's type safety creates a powerful development experience:
- Catch Errors Early: Type checking prevents runtime errors from typos and schema mismatches
- IDE Support: Full autocomplete for table names, column names, and query methods
- Refactoring Confidence: Rename columns or tables and let TypeScript guide you through the changes
- Self-Documenting Code: The types serve as documentation for your data structures
Checkout the repo on github: https://github.com/hypequery/hypequery
Top comments (0)