JavaScript
@gizmodata/hardcharger is the TypeScript client
for Hardcharger™. It runs wherever fetch does (Node, browsers, Deno, Bun, Cloudflare Workers)
with no native code, takes the same hardcharger:// URLs as the Python and
Go drivers, and returns apache-arrow Tables
or streams of record batches.
It is a thin layer on @gizmodata/warp-sql, the
generic JavaScript client for any Warp SQL server, which it depends on and re-exports: import
everything from @gizmodata/hardcharger.
Query, stream, ingest
Section titled “Query, stream, ingest”import { tableFromArrays } from 'apache-arrow';import { HardchargerError, connect } from '@gizmodata/hardcharger';
const conn = await connect({ url: 'hardcharger://localhost?tls=false', username: 'hardcharger', password: 'change-me' });
// A query with a bind parameter: an Arrow Table back.const table = await conn.query('SELECT 42 AS answer, ?::VARCHAR AS greeting', ['hello']);console.log(table.toArray().map((row) => row.toJSON()));
// A big result, streamed batch by batch (and read over several connections at once).let rows = 0;for await (const batch of conn.stream('SELECT * FROM range(10000000)')) rows += batch.numRows;console.log(`streamed ${rows} rows`);
// Bulk ingest from Arrow, then a prepared statement run once per row.await conn.ingest('events', tableFromArrays({ id: Int32Array.from([1, 2, 3]) }), { mode: 'replace' });const insert = await conn.prepare('INSERT INTO events VALUES (?)');console.log(`inserted ${await insert.executeBatch([[4], [5]])} rows`);
// Errors carry the server's code; an AbortSignal cancels the statement on the server.try { await conn.query('SELECT sum(a.range * b.range) FROM range(100000000) a, range(100000) b', undefined, { signal: AbortSignal.timeout(1000), });} catch (err) { if (!(err instanceof HardchargerError)) throw err; console.log(`${err.code}: ${err.message}`);}
await conn.close();[ { answer: 42, greeting: 'hello' } ]streamed 10000000 rowsinserted 2 rowsstatement_timeout: the operation timed out (its AbortSignal fired)| Call | Returns |
|---|---|
connect({ url, username, password }) (or token, tokenProvider, subjectToken for SSO) |
a Connection |
conn.query(sql, params?, options?) |
an Arrow Table (empty for DDL and DML) |
conn.stream(sql, params?, options?) |
a stream to for await over: schema(), rowsAffected(), toTable(), close() |
conn.execute(sql, params?) |
the rows changed |
conn.prepare(sql) |
a prepared statement: query, stream, execute, executeBatch(rows), close() |
conn.ingest(table, data, { mode, catalog, schema, temporary }) |
the rows written |
conn.metadata.catalogs(), tables(), columns(), … |
Arrow tables in Flight SQL’s schemas |
conn.transaction(async (tx) => …) |
commits, or rolls back on a throw |
Statement options (per call, or as defaults in connect): signal, async, retainResult,
statementTimeoutMs, queryTag, resultPartitions (8 in Node, 4 in browsers), resultOrder,
ipcCompression: 'zstd'. Every error is a HardchargerError (the Warp SQL client’s
WarpSqlError, under Hardcharger’s name) with the server’s stable code,
status, sqlstate and requestId. A result stream that ends without Arrow’s end-of-stream
marker throws: a short table is never taken for a complete one.
| URL | Meaning |
|---|---|
hardcharger://host |
HTTPS to host:31337 |
hardcharger://host:port |
HTTPS to another port |
hardcharger://host?tls=false |
Plain HTTP (development, loopback) |
hardcharger://host:port/custom/prefix |
The server mounts Warp SQL under /custom/prefix |
warp://…, https://…, http://… |
The Warp SQL client’s own spellings |
hardcharger+http://, credentials in the URL and unknown parameters (useEncryption included)
are refused before anything is sent, with an invalid_argument error that says what to write.
In Node, Deno and Bun, connect({ url: 'hardcharger://db.example.com', tls: { ca, cert, key } })
takes PEM text for a private CA and mutual TLS. With authType: 'client_certificate' (or 'mtls')
and no password, the client certificate is the credential, on a server with
--mtls-certificate-sign-in (TLS). Browsers use the system’s trust
store.
In the browser
Section titled “In the browser”The same code runs in a page. The server must allow the page’s origin with
cors_origins: an explicit list, off by default, never *:
$ curl -s -o /dev/null -D - -X OPTIONS localhost:31337/warp/v1/auth/token \ -H 'Origin: https://app.example.com' -H 'Access-Control-Request-Method: POST' \ | grep -i '^access-control-allow-origin' | tr -d '\r'Access-Control-Allow-Origin: https://app.example.com$ curl -s -o /dev/null -D - -X OPTIONS localhost:31337/warp/v1/auth/token \ -H 'Origin: https://elsewhere.example.com' -H 'Access-Control-Request-Method: POST' \ | grep -i '^access-control-allow-origin' || echo "no CORS headers: the browser refuses"no CORS headers: the browser refuses- Credentials are bearer tokens in a header, never cookies, so cross-site request forgery has
nothing to ride on. Keep tokens in memory, not in
localStorage. - Serve the page and Hardcharger over HTTPS: browsers block mixed content.
- For people who sign in with your identity provider, pass its ID token as
subjectToken(SSO).
Without the client
Section titled “Without the client”Warp SQL is plain HTTP, so fetch and apache-arrow alone work too, as in
your first query and the plain HTTP page.