Skip to content

About

JavaScript and TypeScript SDK for WaveHouse (@wavehouse/sdk)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

@wavehouse/sdk

TypeScript client for WaveHouse — schema-aware real-time API gateway for ClickHouse. One runtime dependency (eventsource-parser, ~1.4 KB gzipped, itself dependency-free).

Installation

npm install @wavehouse/sdk

Requires Node 22 or newer — the only line this SDK is tested against; Node 18 and 20 are past upstream end-of-life. Neither browsers nor Node need a polyfill: streaming runs on fetch, like the rest of the SDK — see Runtime support.

This works in any framework that uses a bundler — React, Vue, Svelte, Angular, Astro, SolidJS, or plain Vite — with import { createClient } from '@wavehouse/sdk'. The SDK and the server share the SSE wire protocol, so move them as a pair — a skewed pair delivers no usable rows and raises no error (details).

The package is published with npm provenance — npm shows a verified provenance badge on the package page, and npm audit signatures checks the attestation against this repository's signed build.

Use without a build step (CDN)

No bundler, no npm, no framework required — drop the SDK straight into an HTML file you deploy over FTP, object storage, or any static host.

ES module (recommended). Modern browsers run type="module" natively:

<script type="module">
  import { createClient } from 'https://esm.sh/@wavehouse/sdk';

  const wh = createClient({ baseURL: 'https://your-wavehouse.example.com' });
  const { data, error } = await wh.from('clicks').select('page').limit(10);
  console.log(data ?? error);
</script>

Pin a version for production (https://esm.sh/@wavehouse/sdk@0.2.0). jsDelivr (https://cdn.jsdelivr.net/npm/@wavehouse/sdk/+esm) and unpkg (https://unpkg.com/@wavehouse/sdk?module) serve the same ES module. The SDK and the server share the SSE wire protocol, so pin them as a pair — a skewed pair delivers no usable rows and raises no error.

Classic global (<script src>). For pages that can't use ES modules, the bundled IIFE build attaches everything to a WaveHouse global:

<script src="https://cdn.jsdelivr.net/npm/@wavehouse/sdk"></script>
<script>
  const wh = WaveHouse.createClient({ baseURL: 'https://your-wavehouse.example.com' });
  wh.from('clicks').select('page').limit(10).then(({ data }) => console.log(data));
</script>

Versioning. A bare CDN URL serves the latest published release; pin for production (@wavehouse/sdk@0.2.0) or float on a range (@0 for the newest 0.x, @0.2 for 0.2.x). Builds from main are published under the dev tag — @wavehouse/sdk@dev — for trying unreleased changes. Whichever you choose, move the SDK and the server together: they share the SSE wire protocol, and a skewed pair delivers no usable rows and raises no error — a newer SDK drops every row, an older one hands you an event per frame with data: undefined.

Streaming (.stream()) runs on fetch, so it works in both forms with no polyfill.

Quick Start

import { createClient } from '@wavehouse/sdk';

const wh = createClient({
  baseURL: 'http://localhost:8080',
  auth: async () => getAccessToken(), // omit for public access
});

baseURL may include a path prefix (https://app.example.com/api/wavehouse) when WaveHouse is served under one — see Serving under a path prefix.

Query Data

// Structured query with the builder
const { data, error } = await wh
  .from('clicks')
  .select('page', 'button')
  .where('score', '>', 10)
  .count('*', 'total')
  .groupBy('page', 'button')
  .orderBy('total', 'desc')
  .limit(50)
  .timeRange('received_timestamp', '1h');

// Raw SQL (requires admin role)
const { data } = await wh.sql('SELECT page, count() FROM clicks GROUP BY page LIMIT 10');

// Named pipe
const { data } = await wh.pipe('top_pages', { start_date: '2026-01-01', limit: 50 });

Insert Data

const { error } = await wh.from('clicks').insert({ page: '/home', button: 'signup', score: 42.5 });

Stream Real-Time Events

// Callback pattern
const stream = wh.from('clicks').stream();
const unsub = stream.subscribe({
  next: (event) => console.log(event.data),
  status: (state) => console.log(state), // 'connecting' | 'live' | 'reconnecting' | 'closed'
});

// Async iterator pattern
for await (const event of wh.from('clicks').stream()) {
  console.log(event.table, event.data);
}

Pagination

let result = await wh.from('clicks').select().orderBy('received_timestamp', 'desc').limit(100);

while (result.hasMore && result.next) {
  result = await result.next();
}

Type Safety

Pass a Database type for autocomplete on table names and row shapes:

interface Database {
  clicks: { page: string; button: string; score: number; received_timestamp: string };
  users: { id: string; name: string; email: string };
}

const wh = createClient<Database>({ baseURL: '...' });
const { data } = await wh.from('clicks').select('page').limit(10);
// data: Array<{ page: string; button: string; ... }> | null

Error Handling

Async request methods return a Result<T> object — destructure it as { data, error } — and never throw for anything the server returns. .stream() and .liveQuery() return controllers instead, reporting failures through the subscriber's error callback.

The SDK does throw on caller and environment errors: a non-absolute baseURL (REST calls reject with a TypeError; streams report a terminal SSE_CONNECT_ERROR), .stream() / .liveQuery() in a runtime with no global fetch and no options.fetch, and an auth callback that rejects — a token-refresh failure propagates out of the REST call, and on a stream is reported as a retryable SSE_AUTH_ERROR. A dropped stream is re-dialed by the SDK, on a jittered backoff; only non-retryable failures end it. One more exception can escape an SDK call, though it is yours rather than ours: a subscriber's status handler throwing on the first, synchronous .subscribe() / .liveQuery() call — see Error Handling.

const { data, error } = await wh.from('clicks').fetch();
if (error) {
  console.error(error.code, error.message, error.retryable);
  return;
}
// data is guaranteed non-null here

Codegen

Generate TypeScript types from your live WaveHouse schema. The package ships a wavehouse-codegen bin, so after installing you can run it with npx:

npx wavehouse-codegen --url http://localhost:8080 --out ./src/db.d.ts

This introspects /v1/ops/schema, maps ClickHouse column types to TypeScript, and outputs a Database interface you can pass to createClient<Database>(). /v1/ops/schema is admin-only — pass an admin-role token with --auth <jwt> (or -a) against any non-dev policy. Over a nested settings directory the route admits the operator key alone: pass it with --operator-key <key> (or -k) and name the tenant with --tenant <id> (or -t); without --tenant it reads tenant 0's schema.

Development & Testing

Clone, then pnpm install (Node 22 or newer). make ci runs everything CI runs; make fix formats and applies safe lint fixes.

Unit Tests

Unit tests mock all HTTP calls — no backend required.

pnpm test               # run unit tests (no backend needed)
pnpm run test:watch     # watch mode
pnpm run test:coverage  # coverage report, gated at a 40% statement floor

Built Package Smoke Check

make smoke imports and require()s the built package by name (through its exports map) and runs the IIFE bundle as a classic script, at the oldest Node that engines.node admits, and checks that all three export the same names. It also type-checks a CommonJS and an ESM consumer of the package with tsc --module node16, so a CommonJS project ("type": "commonjs") gets its own .d.cts declarations rather than TS1479. It downloads that Node once into .bin/. pnpm run smoke:dist runs the same check on the Node you are running.

Issues

Report SDK bugs at Wave-RF/wavehouse-sdk-js/issues. Server bugs go to Wave-RF/WaveHouse. Until this repository existed the SDK lived in the WaveHouse repository under clients/ts, and its history up to 0.2.0 is carried over here.

API Reference

See the full SDK API Reference for detailed documentation of every method, type, and option.

License

Apache-2.0 © Wave RF — see LICENSE.

About

JavaScript and TypeScript SDK for WaveHouse (@wavehouse/sdk)

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages