RowBinary Logs
+
+ Server-rendered from ClickHouse, decoded with{" "}
+ @clickhouse/rowbinary. The browser only receives HTML —
+ all decoding happens in lib/logs.ts on the server.
+
+
+A tiny, **server-only** Next.js app that pages through a ClickHouse logs table,
+decoding each page from `RowBinary` with
+[`@clickhouse/rowbinary`](../../). The browser receives only HTML — there are no
+client components and no client-side data fetching. All decoding happens on the
+server in [`lib/logs.ts`](lib/logs.ts).
+
+It's wired up so you can poke at the decoder yourself: change the schema, the
+reader, the page size, or swap the API-combinator reader for a monomorphized one.
+
+## What it shows
+
+The `demo_logs` table is intentionally a mix of types that exercise the
+decoder's interesting paths:
+
+| Column | ClickHouse type | Decoded as |
+| ------------- | ------------------------- | ------------------------------- |
+| `timestamp` | `DateTime64(3)` | JS `Date` (ms-lossless) |
+| `level` | `Enum8('debug'..'error')` | `number` → name at the edge |
+| `service` | `LowCardinality(String)` | plain `String` (transparent) |
+| `host` | `IPv4` | dotted-quad `string` |
+| `trace_id` | `UUID` | canonical `8-4-4-4-12` `string` |
+| `status` | `UInt16` | `number` |
+| `duration_ms` | `Float64` | `number` |
+| `message` | `String` | `string` |
+
+The per-column reads live in `readLogRow` in [`lib/logs.ts`](lib/logs.ts) — one
+leaf reader per column, in wire order. That's the clear, "correct by default"
+combinator form; the comments point at where you'd monomorphize it if this were a
+hot path.
+
+## Prerequisites
+
+- Node 18+ (built/tested on Node 24)
+- A running ClickHouse. This demo ships a self-contained one — from **this
+ directory** (`demo/logs`):
+
+ ```bash
+ docker compose up -d
+ ```
+
+ That exposes HTTP on `localhost:8123` with the default user and no password.
+
+## Run it
+
+From this directory (`demo/logs`):
+
+```bash
+npm install # installs Next + the local @clickhouse/rowbinary tarball
+npm run seed # create demo_logs and insert 1000 rows (pass a number to change: npm run seed -- 50000)
+npm run dev # http://localhost:3000
+```
+
+Then open
+ Server-rendered from ClickHouse, decoded with{" "}
+ @clickhouse/rowbinary. The browser only receives HTML —
+ all decoding happens in lib/logs.ts on the server.
+
| Timestamp | +Level | +Service | +Status | +Duration | +Host | +Trace ID | +Message | +
|---|---|---|---|---|---|---|---|
| {fmtTime(row.timestamp)} | ++ {row.level} + | +{row.service} | +{row.status} | +{row.durationMs.toFixed(1)} ms | +{row.host} | +{row.traceId.slice(0, 8)}… | +{row.message} | +
No logs to show{message ? `: ${message}` : "."}
+Make sure ClickHouse is running and the table is seeded:
+{`# from demo/logs\ndocker compose up -d\nnpm run seed`}
+