Compare
hypequery vs the ClickHouse HTTP Interface
The ClickHouse HTTP interface is a great way to run queries from the shell. hypequery uses the same protocol underneath and adds types, bound parameters, and reuse for application code. Here's when to reach for which.
Decision type
Architecture and workflow fit
Audience
TypeScript teams building on ClickHouse
Outcome
Choose a path and move into implementation
Best for
Application code that queries ClickHouse at request time
Parameters
Bound parameters via the query builder
Response types
Generated from your schema, correct runtime mappings
Reuse
One query definition across execution, HTTP, and React
Exposed endpoints
zod validation, auth hooks, OpenAPI via @hypequery/serve
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.
If you run ClickHouse, you already know the HTTP interface. It's the first thing that works: point curl at port 8123, pass a query, ask for FORMAT JSON, and you get data back.
Short answer: don't replace this for what it's good at. The HTTP interface is simple, fast, and dependency-free, and it's the right tool for scripts, health checks, and one-off queries. What breaks down is using it directly from application code — where the same query needs runtime parameters, correct types, and a definition you can reuse. hypequery speaks the same HTTP protocol underneath (it's built on @clickhouse/client), so this isn't a protocol argument. It's about what sits on top of the protocol.
What the HTTP interface does well
The ClickHouse HTTP interface is genuinely excellent, and it's worth being specific about why:
- Zero dependencies. Any language with an HTTP client can talk to it. No driver, no ORM, no build step.
- Fast and direct. You're talking to the server with nothing in the middle. Latency is the network plus the query.
- Great for automation. Health checks (
SELECT 1), CI probes, cron jobs, quick data pulls — the HTTP endpoint is the obvious right answer for all of them. - Flexible output.
FORMAT JSON,JSONEachRow,CSV,TSV,Pretty— one endpoint, many shapes.
If your task is "check the cluster is up" or "pull yesterday's totals into a spreadsheet," stop reading. curl is the tool.
Where curl-over-HTTP stops scaling
The trouble starts when the HTTP interface moves from your terminal into your backend, and the query stops being a fixed string. Four things go wrong, and they compound.
1. String-concatenated SQL
A real endpoint takes input. A region filter, a date range, a tenant ID. With the HTTP interface, that means building the SQL string yourself:
That interpolation is a SQL injection hole, and even when it isn't malicious it's an escaping bug waiting to happen — a region name with an apostrophe breaks the query. ClickHouse does support server-side bound parameters over HTTP (param_region), but wiring them by hand across every endpoint is exactly the boilerplate that gets skipped under deadline.
2. No types on the response
FORMAT JSON gives you { data: [...], meta: [...], rows: N }. Every field is whatever JSON says it is, and you annotate it by hand:
That cast is a promise you're making to the compiler with nothing backing it. And it's frequently wrong in ClickHouse-specific ways: UInt64 comes back as a string, not a number; DateTime is a string; Nullable(T) can be null. Hand-written types drift from the real schema, and the mismatch surfaces at runtime.
3. No reuse
The "revenue by region" query is needed in the dashboard endpoint, the export job, and the internal admin view. Over raw HTTP, that's the same SQL string pasted in three places. When the definition of revenue changes, you change it three times and hope you found them all. This is the exact failure mode covered in stop writing the same query three times.
4. DIY governance on anything exposed
The moment an HTTP-backed query sits behind a route users can hit, you own validation, auth, and rate limiting yourself. There's no schema on the input, no place to enforce a tenant boundary, no generated API contract for consumers. All of it is hand-rolled per endpoint.
Same protocol, typed contract on top
hypequery doesn't bypass the HTTP interface — it uses it, through @clickhouse/client, which is the official Node client that talks the same HTTP protocol to ClickHouse. What hypequery adds is the layer between that transport and your application.
You generate types from your live schema once:
Then the same "revenue by region, filtered by input" query looks like this:
The where value is a bound parameter, not string interpolation, so injection and escaping aren't your problem. rows is typed from the generated schema, including the runtime realities — revenue is typed to match how ClickHouse actually returns the aggregate, and a UInt64 column would come back as a string, correctly typed. The query is one definition you import wherever it's needed.
And when that query needs to be an endpoint, @hypequery/serve gives you validation, auth hooks, and OpenAPI docs on the same definition — see /clickhouse-rest-api and /clickhouse-openapi:
The input is validated by zod before any SQL runs, the response type flows through to callers, and the same contract is consumed by React hooks on the dashboard.
The honest comparison
| ClickHouse HTTP interface | hypequery | |
|---|---|---|
| Transport | HTTP to port 8123 | Same HTTP, via @clickhouse/client |
| Best for | Scripts, health checks, one-offs | Application code at request time |
| Parameters | You build the SQL string | Bound parameters via the builder |
| Response types | Untyped JSON, hand-annotated | Generated from your schema |
| Runtime mappings | You remember UInt64 is a string | Encoded in the generated types |
| Reuse | Copy the query string | One imported definition |
| Exposed endpoints | DIY validation and auth | zod, auth hooks, OpenAPI via serve |
| Dependencies | None | An npm dependency |
When the HTTP interface is still the right call
Be clear-eyed about this: hypequery is a dependency in your application, and there are jobs where that's overhead you don't want.
- Health checks and probes.
curl 'http://host:8123/?query=SELECT%201'is perfect. Don't wrap it. - Ad-hoc exploration. Poking at data from a shell or a notebook — the HTTP endpoint is faster to reach for than any library.
- Non-TypeScript scripts. A Python cron job or a Go health checker has no reason to adopt a TypeScript query layer.
- Bulk exports and inserts. Streaming a large result or loading data is transport-level work; the client and the HTTP interface handle it directly.
hypequery earns its place when queries live in your TypeScript application, take runtime input, need correct types, and get reused. It's not either/or — the same protocol runs underneath either way. For the deeper look at the client hypequery builds on, see hypequery vs @clickhouse/client and the clickhouse-js guide.
Getting started
The quick start takes you from your existing ClickHouse — the same server you've been curl-ing — to generated types and a served, typed endpoint in a few minutes. If you want more background first, /clickhouse-api covers the interface options, and /clickhouse-rest-api covers putting a governed API in front of your queries.
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 hypequery replace the ClickHouse HTTP interface?
No. hypequery builds on @clickhouse/client, which speaks the same HTTP protocol to ClickHouse. It adds generated types, bound parameters, and reusable query definitions on top of the same transport.
When should I stick with raw curl over HTTP?
For health checks, ad-hoc exploration, shell scripts, and CI probes, curl against port 8123 with FORMAT JSON is the right tool. There is nothing to install and nothing to maintain.
Is string-concatenated SQL over HTTP a real risk?
Yes, once user input reaches the query. Interpolating values into a SQL string exposes you to injection and escaping bugs. hypequery's query builder binds parameters and validates inputs before any SQL runs.
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.