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.
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
Userclass, and TypeORM handles the mapping to auserstable, 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 … COMMITyou'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
ReplacingMergeTreeyou dedupe at merge time (eventually) or force it at query time withFINAL— 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 generateand get TypeScript types from your live ClickHouse schema, with the runtime realities baked in:UInt64comes back as a string,DateTimeas a string,Nullable(T)asT | null. - A composable query builder. Filters, aggregations, joins, time grains, and tenant scoping, all typed against your schema.
- Typed serving.
@hypequery/serveturns a query into a validated REST endpoint with generated OpenAPI docs. - React hooks.
@hypequery/reactconsumes 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
| TypeORM | hypequery | |
|---|---|---|
| Built for | OLTP row stores (Postgres, MySQL, SQLite) | ClickHouse analytics |
| ClickHouse support | None — no driver, no workaround | Native |
| Core abstraction | Entities, relations, identity map | Typed queries, measures, dimensions |
| Transactions | Yes — a core feature | No — ClickHouse has no unit-of-work model |
| Schema source | Decorated entity classes → migrations | Introspected from live ClickHouse |
| Type mapping | OLTP column types | ClickHouse-correct runtime types |
| Serving layer | None (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
- TypeORM and ClickHouse — the fuller version of why there's no driver, and what to use instead.
- Does ClickHouse have an ORM? — the category mismatch, spelled out.
- Query builder vs ORM for ClickHouse — why the query-builder shape fits columnar analytics.
- ClickHouse ORM — the pillar page on the whole "ORM for ClickHouse" question.
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.