DBDelve
Dive deep into your data. A fast, native, multi-engine database client written in Rust. Free and open source, no telemetry.
Client SDK — query builder, real-time streaming, codegen.
Translation pending
Developer toolsTypeScript · ShellGitHub @Wave-RFOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
JavaScript and TypeScript SDK for WaveHouse (@wavehouse/sdk)
Loading the full README.
Read the full README ↗TypeScript client for WaveHouse — schema-aware real-time API gateway for ClickHouse. One runtime dependency (eventsource-parser, ~1.4 KB gzipped, itself dependency-free).
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.
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/[email protected]). 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/[email protected]) 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.
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.
// 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 });
const { error } = await wh.from('clicks').insert({ page: '/home', button: 'signup', score: 42.5 });
// 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);
}
let result = await wh.from('clicks').select().orderBy('received_timestamp', 'desc').limit(100);
while (result.hasMore && result.next) {
result = await result.next();
}
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
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.
This is the beginning of the README. Read the rest at the original. Read the full README ↗