Compare

hypequery vs TypeORM for ClickHouse

TypeORM has no ClickHouse driver and no real workaround — its entity model doesn't fit columnar storage. hypequery is the ClickHouse-native TypeScript layer, and the practical answer is to run both side by side.

Decision type

Architecture and workflow fit

Audience

TypeScript teams building on ClickHouse

Outcome

Choose a path and move into implementation

ClickHouse support

Native — built specifically for ClickHouse

Data model

Columnar analytics: aggregations, time grains, tenant scoping

Schema source

Generated from your live ClickHouse schema

Type mapping

ClickHouse-correct (UInt64 → string, DateTime → string, Nullable → T | null)

Analytics layer

Query builder, HTTP serving, OpenAPI, React hooks

What this page is for

Use this page when the real question is how one tool changes the shape of your ClickHouse application code, not just which syntax looks nicer.

What this page is not

It is not a broad ecosystem roundup. It stays narrow on the tradeoff a ClickHouse-heavy TypeScript team is actually deciding.

Recommended next move

If the tradeoff already looks clear, stop reading comparisons and test the fit against one real query in your own schema.

Dimension
hypequery
Alternative
ClickHouse support
Native — built specifically for ClickHouse
None — Postgres, MySQL, SQLite, and other OLTP engines only
Data model
Columnar analytics: aggregations, time grains, tenant scoping
Relational entities: FKs, transactions, migrations, identity map
Schema source
Generated from your live ClickHouse schema
Decorated entity classes, migrated to the database
Type mapping
ClickHouse-correct (UInt64 → string, DateTime → string, Nullable → T | null)
Column types for OLTP engines; no ClickHouse column set
Analytics layer
Query builder, HTTP serving, OpenAPI, React hooks
Repository/entity CRUD — no analytics serving layer
Back to comparisons

Short answer

TypeORM does not support ClickHouse. There's no driver, and unlike Prisma or Drizzle there isn't even an experimental MySQL-port trick worth trying. That's not a gap TypeORM will close soon, and it's not a bug — TypeORM is an entity ORM built for transactional row stores, and ClickHouse is a columnar, append-only analytics database. The two models genuinely don't fit.

So this isn't really a "which one wins" comparison. TypeORM is fine at its actual job. hypequery is the ClickHouse-native TypeScript layer for the analytics side. The realistic outcome is that you run both: TypeORM stays on Postgres or MySQL, and hypequery handles ClickHouse. This page explains why the ORM model doesn't map onto ClickHouse, and shows the two working side by side in one service.

If you just want to see hypequery run, the quick start goes from a live ClickHouse schema to a typed query in a few minutes.

What TypeORM is good at

TypeORM is a mature, well-liked ORM for Node.js, and it earns its place in a lot of Nest and Express apps. Give it credit for what it does well:

  • Entities as classes. You decorate a User class, and TypeORM handles the mapping to a users table, relations to other entities, and lazy loading.
  • Transactions. Wrap a set of writes in a transaction and get atomic commit/rollback — exactly what you want when creating an order and its line items together.
  • Migrations. Schema changes are generated and versioned from your entity definitions.
  • The identity map. Within a unit of work, the same database row is the same object in memory, which keeps mutation sane.
  • Relational integrity. Foreign keys, cascades, and joins across entities are first-class.

Every one of those features assumes a database that does row-level updates, enforces constraints, and runs transactions. That's Postgres, MySQL, SQLite, SQL Server, and friends. It is precisely not ClickHouse.

Why the ORM model doesn't map onto ClickHouse

ClickHouse is columnar and optimized for reading huge ranges of rows and aggregating them fast. It makes deliberate tradeoffs that break the assumptions an ORM is built on:

  • No real transactions. There's no BEGIN … COMMIT you'd wrap a unit of work in. TypeORM's transaction API has nothing to bind to.
  • Append-optimized, mutation-averse. Updates and deletes exist but are asynchronous background operations (ALTER TABLE … UPDATE), not the row-level UPDATE an identity map assumes. The whole "load an entity, mutate it, flush it back" loop doesn't apply.
  • No enforced foreign keys. ClickHouse doesn't enforce referential integrity. The relation graph an ORM manages simply isn't there to manage.
  • Deduplication is a merge-time concern. With ReplacingMergeTree you dedupe at merge time (eventually) or force it at query time with FINAL — there's no unique constraint doing it for you on write.
  • Engine choice is the schema. MergeTree, ReplacingMergeTree, SummingMergeTree, sorting keys, partitioning — these are the meaningful decisions in ClickHouse, and TypeORM's decorator model has no vocabulary for them.

This is the same reason there's no Prisma for ClickHouse and no Drizzle driver for it either. ORMs model row-level transactional entities; ClickHouse is aggregation-first. It's a category mismatch, not a missing feature. We go deeper on that distinction in query builder vs ORM for ClickHouse.

What hypequery does instead

hypequery doesn't try to be an ORM for ClickHouse — it's a typed query builder and serving layer that leans into the columnar model instead of fighting it:

  • Generated types. Run hypequery generate and get TypeScript types from your live ClickHouse schema, with the runtime realities baked in: UInt64 comes back as a string, DateTime as a string, Nullable(T) as T | null.
  • A composable query builder. Filters, aggregations, joins, time grains, and tenant scoping, all typed against your schema.
  • Typed serving. @hypequery/serve turns a query into a validated REST endpoint with generated OpenAPI docs.
  • React hooks. @hypequery/react consumes the same typed contract in dashboards.

Here's an analytics query — revenue per day for one tenant — that has no clean TypeORM equivalent:

That's a GROUP BY toStartOfDay(created_at) aggregation — the kind of query ClickHouse exists to run and an entity ORM was never meant to express.

The honest comparison

TypeORMhypequery
Built forOLTP row stores (Postgres, MySQL, SQLite)ClickHouse analytics
ClickHouse supportNone — no driver, no workaroundNative
Core abstractionEntities, relations, identity mapTyped queries, measures, dimensions
TransactionsYes — a core featureNo — ClickHouse has no unit-of-work model
Schema sourceDecorated entity classes → migrationsIntrospected from live ClickHouse
Type mappingOLTP column typesClickHouse-correct runtime types
Serving layerNone (it's a data-access ORM)REST + OpenAPI + React hooks

Coexistence: TypeORM and hypequery in one service

The common real-world shape: transactional data lives in Postgres or MySQL (users, orders, subscriptions) and event/analytics data lives in ClickHouse (usage events, aggregated metrics). TypeORM owns the first; hypequery owns the second. They don't overlap, so they coexist cleanly in a single Nest or Express app.

Each tool does what it's built for. TypeORM guarantees the write is atomic; hypequery aggregates millions of rows in the read path with types that match what ClickHouse actually returns. Neither is pretending to be the other.

If you want to expose that analytics read as a governed HTTP route, @hypequery/serve wraps it with zod validation and OpenAPI docs — so the same query is usable in-process and over HTTP without hand-writing response types.

When TypeORM is the right call

Don't reach for hypequery where TypeORM belongs. Stay on TypeORM when:

  • Your data is transactional entities with relations and constraints.
  • You need atomic multi-row writes.
  • You're on Postgres, MySQL, SQLite, or another engine TypeORM supports.
  • The work is CRUD and business logic, not analytical aggregation.

And reach for hypequery when the data lives in ClickHouse and the queries are aggregations, time series, and tenant-scoped analytics served to an app.

Where to go from here

When you're ready, the quick start takes you from schema introspection to a typed, served endpoint — the fastest way to see whether the library approach fits the ClickHouse side of your stack.

Decision checkpoint

If the tradeoff is already clear, move into implementation

Most teams do not need another round of comparison content after this point. The better test is whether the workflow holds up on your own schema and your own query complexity.

Related content

Continue into implementation

FAQ

Does TypeORM support ClickHouse?

No. TypeORM has no ClickHouse driver, and there is no MySQL-port style workaround that holds up, because its entity model (foreign keys, transactions, migrations, the identity map) assumes a row-oriented transactional database. hypequery is built natively for ClickHouse instead.

Can I use TypeORM for Postgres and hypequery for ClickHouse in the same app?

Yes — this is the recommended setup. Keep TypeORM on your Postgres or MySQL entities and add hypequery for the ClickHouse analytics side. They run side by side in one Nest or Express service without competing for the same tables.

Is hypequery an ORM?

No. hypequery is a typed query builder and serving layer for ClickHouse, not an entity ORM. ClickHouse is columnar and aggregation-first, so hypequery models measures, dimensions, and time grains rather than entities with relations and lifecycle hooks.

Next step

Move from evaluation into a typed ClickHouse workflow

Generate schema types, define your first reusable query, and decide whether it should run locally or over HTTP.