npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hyperdb-api-node

v0.1.3

Published

Node.js bindings for the Hyper database API, powered by napi-rs

Readme

hyperdb-api-node

Node.js bindings for the Hyper database API, powered by napi-rs.

This package provides a native Node.js addon that gives JavaScript and TypeScript developers high-performance access to Hyper database files (.hyper) using the pure Rust hyperdb-api crate under the hood.

Features

  • Async-first — all I/O methods return Promises
  • Tagged template literalsconn.sql\SELECT * FROM t WHERE id = ${id}`` — safe by default
  • Apache Arrow integration — query results as Arrow Tables at 15M+ rows/sec
  • Native performance — compiled Rust, no FFI overhead at runtime
  • Full TypeScript support — hand-written .d.ts declarations with full IntelliSense
  • Connection pooling — configurable min/max, idle timeout, acquire timeout
  • Multiple query APIs — row-oriented, columnar, Arrow IPC, tagged templates
  • Resource managementSymbol.asyncDispose for await using (Node 22+)
  • Query tracingconn.on('query', ...) hooks for logging/metrics
  • Cross-platform — macOS (ARM & x64), Linux, Windows

Class Overview

HyperProcess ──creates──▶ Connection ──returns──▶ RowData
ConnectionBuilder ─builds─▶ Connection ──returns──▶ QueryStream ──yields──▶ RowData
ConnectionPool ──pools──▶ Connection ──returns──▶ ColumnarStream ──yields──▶ ColumnarChunk

Catalog ──uses──▶ Connection          TableDefinition ──uses──▶ SqlType
Inserter ──uses──▶ Connection + TableDefinition

For a detailed class diagram with all methods and properties, see DEVELOPMENT.md.

Requirements

  • Node.js >= 21
  • Hyper server binary (hyperd) — set HYPERD_PATH env var or place in standard location

Installation

npm install hyperdb-api-node

npm will automatically install the correct prebuilt binary for your platform via optionalDependencies — no Rust toolchain required.

Supported platforms:

| Platform | Architecture | Package | |---|---|---| | macOS | ARM64 (Apple Silicon) | hyperdb-api-node-darwin-arm64 | | Linux | x64 (glibc) | hyperdb-api-node-linux-x64-gnu | | Linux | x64 (musl/Alpine) | hyperdb-api-node-linux-x64-musl | | Linux | ARM64 (glibc) | hyperdb-api-node-linux-arm64-gnu | | Windows | x64 (MSVC) | hyperdb-api-node-win32-x64-msvc |

macOS x64 (Intel) is not currently published while we wait for macos-13 GitHub Actions runners to become reliably available. Build from source for Intel Macs in the meantime.

Build from source

To build from the workspace source instead, you'll need:

  • Node.js >= 21
  • Rust stable (install via rustup)
  • protocbrew install protobuf (macOS), apt-get install protobuf-compiler (Debian/Ubuntu), or choco install protoc (Windows)
  • The hyperd binary (make download-hyperd from the repo root, or set HYPERD_PATH to an existing copy)
git clone <repo-url>
cd hyper-api-rust/hyperdb-api-node
npm install
npm run build       # release build; use `npm run build:debug` for faster iteration

This produces hyperdb-api-node.<platform-tag>.node next to index.js. To use it from another local project:

cd ../my-app
npm install ../hyperdb-api-node      # file: dependency

See DEVELOPMENT.md for cross-compilation, prebuild orchestration, npm publishing flow, and troubleshooting.

Quick Start

TypeScript (.mts)

import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const {
  HyperProcess, Connection, CreateMode, Catalog,
  TableDefinition, SqlType, Inserter,
} = require('hyperdb-api-node') as typeof import('hyperdb-api-node');
import type { RowData } from 'hyperdb-api-node';

const hyper = new HyperProcess();
const conn = await Connection.connect(hyper.endpoint, 'example.hyper', CreateMode.CreateIfNotExists);

// Define and create a table
const tableDef = new TableDefinition('users');
tableDef.addColumn('id', SqlType.int(), false);       // NOT NULL
tableDef.addColumn('name', SqlType.text(), true);      // NULLABLE
tableDef.addColumn('score', SqlType.double(), true);
await new Catalog(conn).createTable(tableDef);

// Insert data
const inserter = new Inserter(conn, tableDef);
inserter.addRows([[1, 'Alice', 95.5], [2, 'Bob', null], [3, 'Charlie', 88.0]]);
console.log(`Inserted ${await inserter.execute()} rows`);

// Query with tagged template (safe by default)
const rows: RowData[] = await conn.sql`SELECT * FROM users WHERE score > ${80}`;
for (const row of rows) {
  console.log(`id=${row.getInt32(0)}, name=${row.getString(1)}, score=${row.getFloat64(2)}`);
}

await conn.close();
hyper.close();

JavaScript (.mjs)

The same code works in plain JavaScript -- just change the require() call:

const { HyperProcess, Connection, CreateMode, /* ... */ } = require('hyperdb-api-node');

See examples/complete-api-tour.mjs for a full JavaScript walkthrough.

API Reference

HyperProcess

Manages a local Hyper server process (hyperd).

const hyper = new HyperProcess();             // auto-detect hyperd location
const hyper = new HyperProcess('/path/to/hyperd'); // or specify path

hyper.endpoint;  // e.g., "localhost:7483"
hyper.isOpen;    // true

// Convenience: connect directly
const conn = await hyper.connectToDatabase('test.hyper', CreateMode.CreateIfNotExists);

hyper.close();   // MUST be called when done

Connection

An async connection to a Hyper database. All I/O methods return Promises.

// Connect to a database
const conn = await Connection.connect('localhost:7483', 'db.hyper', CreateMode.CreateIfNotExists);

// With authentication
const conn = await Connection.connectWithAuth(endpoint, 'db.hyper', CreateMode.CreateIfNotExists, 'user', 'pass');

// Without a database (server-level operations)
const conn = await Connection.withoutDatabase('localhost:7483');

// Execute commands (returns affected row count) and queries (returns RowData[])
const affected = await conn.executeCommand("INSERT INTO users VALUES (1, 'Alice')");
const rows = await conn.executeQuery('SELECT * FROM users');
const schema = await conn.querySchema('SELECT * FROM users');
// schema = [{ name: 'id', typeName: 'INTEGER', index: 0 }, ...]

conn.database;  // 'db.hyper' or null
conn.isAlive;   // true
await conn.close(); // MUST be called when done

ConnectionBuilder

Fluent builder for connections with authentication and timeouts. Transport is auto-detected: localhost:7483 -> TCP, https://server:443 -> gRPC.

const conn = await new ConnectionBuilder('localhost:7483')
  .database('my.hyper')
  .createMode(CreateMode.CreateIfNotExists)
  .user('admin').password('secret')
  .loginTimeout(5000)
  .build();

CreateMode

Controls database creation behavior when connecting.

CreateMode.DoNotCreate       // Database must exist
CreateMode.Create            // Create new (fails if exists)
CreateMode.CreateIfNotExists // Create if missing
CreateMode.CreateAndReplace  // Drop and recreate

RowData

A row from a query result. Access values by column index (0-based).

const rows = await conn.executeQuery('SELECT id, name, active FROM users');

for (const row of rows) {
  // Typed accessors (return null for NULL values or type mismatches)
  row.getInt32(0);    // number | null
  row.getInt64(1);    // number | null (caution: precision loss above 2^53)
  row.getFloat64(2);  // number | null
  row.getString(3);   // string | null (converts non-string types to string)
  row.getBool(4);     // boolean | null
  row.getBytes(5);    // Buffer | null

  // Metadata
  row.isNull(0);      // boolean
  row.columnCount;    // number
}

SqlType

SQL column types. Use static factory methods to create instances.

// Numeric
SqlType.bool()                SqlType.smallInt()           SqlType.int()
SqlType.bigInt()              SqlType.float()              SqlType.double()
SqlType.numeric(18, 2)        // NUMERIC(precision, scale)

// String / Binary
SqlType.text()                SqlType.varchar(255)         SqlType.char(10)
SqlType.bytes()               // BYTEA

// Date / Time
SqlType.date()                SqlType.time()               SqlType.timestamp()
SqlType.timestampTz()         SqlType.interval()

// Other
SqlType.json()                SqlType.geography()

SqlType.numeric(18, 2).toString(); // "NUMERIC(18, 2)"

TableDefinition

Defines a table schema for creating tables or bulk inserts.

const tableDef = new TableDefinition('products');
tableDef.withSchema('inventory');
tableDef.addColumn('id', SqlType.int(), false);          // NOT NULL
tableDef.addColumn('name', SqlType.text(), true);         // NULLABLE
tableDef.addColumn('price', SqlType.numeric(10, 2), true);

tableDef.name;         // 'products'
tableDef.schema;       // 'inventory'
tableDef.columnCount;  // 3
tableDef.getColumns(); // [{ name: 'id', typeName: 'INTEGER', nullable: false }, ...]
tableDef.toCreateSql(); // 'CREATE TABLE "inventory"."products" (...)'

Catalog

Database catalog operations: create/drop tables and schemas.

const catalog = new Catalog(conn);

// Schemas
await catalog.createSchema('analytics');
await catalog.getSchemaNames();              // ['public', 'analytics']
await catalog.dropSchema('analytics', true); // cascade=true

// Tables
await catalog.createTable(tableDef);
await catalog.hasTable('public.users');      // true
await catalog.getTableNames('public');       // ['users', 'products']
await catalog.dropTable('public.users');

Inserter

High-performance bulk data inserter using the COPY protocol.

const inserter = new Inserter(conn, tableDef);

inserter.addRow([1, 'Widget', 19.99]);          // one at a time
inserter.addRows([                               // or batch
  [2, 'Gadget', null],
  [3, 'Doohickey', 5.99],
]);

console.log(inserter.bufferedRowCount);          // 3
const count = await inserter.execute();          // send to server, returns row count

// Inserter can be reused after execute()
inserter.addRow([4, 'Another', 7.77]);
await inserter.execute();

QueryStream

Streaming query results for memory-efficient iteration over large result sets. Unlike executeQuery() which loads all rows into memory, QueryStream fetches rows in chunks (~64K rows each).

// Option A: chunk-level iteration (high performance, batch processing)
const stream = conn.executeQueryStream('SELECT * FROM big_table');
let chunk;
while ((chunk = await stream.nextChunk()) !== null) {
  for (const row of chunk) { row.getInt32(0); }
}

// Option B: row-level async iteration (convenient)
for await (const row of conn.executeQueryStream('SELECT * FROM big_table')) {
  console.log(row.getInt32(0), row.getString(1));
}

// Column metadata is available via stream.getSchema() after the first chunk

Type Mappings

Reading (Hyper -> JS):

| SQL Type | Method | JS Type | |---|---|---| | INT | getInt32(i) | number | | BIGINT | getInt64(i) / getBigInt(i) | number / bigint | | DOUBLE | getFloat64(i) | number | | BOOLEAN | getBool(i) | boolean | | TEXT / VARCHAR | getString(i) | string | | BYTEA | getBytes(i) | Buffer | | DATE | getDateMs(i) -> new Date(ms) | number (Unix ms) | | TIMESTAMP / TZ | getTimestampMs(i) -> new Date(ms) | number (Unix ms) | | JSON | getJson(i) -> JSON.parse() | string | | Any type | getString(i) | string (fallback) |

Writing (JS -> Hyper) — via Inserter or tagged templates:

| JS Type | SQL Type | |---|---| | number | INT / BIGINT / DOUBLE (auto-detected) | | string | TEXT | | boolean | BOOLEAN | | null / undefined | NULL | | Buffer | BYTEA | | Date | TIMESTAMP (via toISOString()) | | {} (plain object) | JSON (via JSON.stringify()) |

Tagged Template Literals

The idiomatic way to write safe queries. Values are automatically escaped to SQL literals -- no SQL injection possible:

const rows = await conn.sql`SELECT * FROM users WHERE name = ${name} AND age > ${minAge}`;
await conn.command`INSERT INTO users (id, name) VALUES (${id}, ${name})`;
await conn.command`UPDATE users SET score = ${score} WHERE id = ${id}`;

Supported value types: number, string, boolean, null, Buffer.

Parameterized Queries

$1/$2 placeholder syntax (alternative to tagged templates):

const rows = await conn.executeQueryParams(
  'SELECT * FROM users WHERE name = $1 AND age > $2', ['Alice', 30]
);
await conn.executeCommandParams(
  'INSERT INTO users (id, name, score) VALUES ($1, $2, $3)', [1, 'Alice', 95.5]
);

Supported parameter types: number, string, boolean, null, Buffer. Values are escaped -- no SQL injection possible.

ConnectionPool

A connection pool that manages reusable database connections. Import from hyperdb-api-node/pool.mjs.

import { ConnectionPool } from 'hyperdb-api-node/pool.mjs';

const pool = new ConnectionPool(hyper.endpoint, 'data.hyper', {
  min: 2, max: 10, idleTimeoutMs: 30_000,
});

// Shorthand: auto acquire/release
const rows = await pool.query('SELECT * FROM users');
const users = await pool.queryParams('SELECT * FROM users WHERE age > $1', [18]);

// Custom logic with auto acquire/release
const result = await pool.use(async (conn) => {
  await conn.executeCommand('BEGIN');
  await conn.executeCommandParams('INSERT INTO logs VALUES ($1)', ['event']);
  await conn.executeCommand('COMMIT');
  return 'ok';
});

// Pool stats: pool.size, pool.idle, pool.active, pool.pending
await pool.close();

| Option | Default | Description | |---|---|---| | min | 0 | Minimum idle connections to keep alive | | max | 10 | Maximum total connections | | idleTimeoutMs | 30000 | Close idle connections after this many ms | | acquireTimeoutMs | 30000 | Max ms to wait for a connection (0 = no limit) | | createMode | CreateIfNotExists | Database creation mode |

RowData Extras

row.getBigInt(0);            // lossless 64-bit as BigInt (e.g., 9007199254740993n)
row.toJSON();                // { "0": "1", "1": "Alice", "2": "95.5" }

// Schema-aware keys
row.setColumnNames(schema.map(c => c.name));
row.toJSON();                // { id: "1", name: "Alice", score: "95.5" }

Query Event Hooks

conn.on('query', ({ sql, durationMs, rowCount, type }) => {
  console.log(`[${type}] ${sql} — ${durationMs}ms, ${rowCount} rows`);
});
conn.off('query', listener); // unsubscribe

Resource Management (await using)

On Node.js 22+, Connection supports Symbol.asyncDispose and HyperProcess supports Symbol.dispose:

{
  await using conn = await Connection.connect(endpoint, db, CreateMode.CreateIfNotExists);
  await conn.sql`INSERT INTO t VALUES (${1}, ${'Alice'})`;
} // conn.close() called automatically

{ using hyper = new HyperProcess(); /* ... */ } // hyper.close() called automatically

Error Handling

All async methods throw Error on failure with details from the Rust/Hyper layer:

try { await conn.executeCommand('SELECT * FROM nonexistent'); }
catch (err) { console.error(err.message); }

Apache Arrow Integration

hyperdb-api-node has first-class Apache Arrow support. Hyper serializes query results directly to Arrow IPC format — no intermediate row/column conversion — making this the fastest data extraction path.

# apache-arrow is an optional peer dependency
npm install apache-arrow

Raw IPC Buffers (zero-dependency)

executeQueryToArrow() and exportTableToArrow() return raw Arrow IPC bytes as a Buffer. No apache-arrow package needed:

const buf = await conn.executeQueryToArrow('SELECT * FROM measurements');
writeFileSync('results.arrows', buf); // readable by DuckDB, Polars, pandas

Using arrow.mjs (with apache-arrow)

The convenience module wraps IPC buffers into full Arrow Tables:

import { tableFromQuery, exportTable, queryToArrowFile, insertFromTable } from 'hyperdb-api-node/arrow.mjs';

// Query -> Arrow Table
const table = await tableFromQuery(conn, 'SELECT * FROM sales');
const revenue = table.getChild('revenue').toArray();  // Float64Array

// Export to .arrow file (IPC file format, readable by DuckDB/Polars/pandas/R)
const bytes = await queryToArrowFile(conn, 'SELECT * FROM sales');
writeFileSync('sales.arrow', bytes);

// Insert from Arrow Table
const count = await insertFromTable(conn, tableDef, arrowTable);

| Function | Description | |---|---| | tableFromQuery(conn, sql) | Query -> Arrow Table | | exportTable(conn, tableName) | Export table -> Arrow Table | | batchesFromQuery(conn, sql) | Query -> RecordBatch[] | | queryToArrowFile(conn, sql) | Query -> Arrow IPC file bytes (.arrow) | | insertFromTable(conn, def, table) | Arrow Table -> Hyper insert | | querySchema(conn, sql) | Query -> Arrow Schema (no data) |

Benchmark Results

Measured on Apple M3 Max, Node.js v24, release build, 1M rows.

Table schema: measurements(id INT NOT NULL, sensor_id INT, value DOUBLE, timestamp BIGINT) — 24 bytes/row.

Query Performance

| Benchmark | Rows/sec | MB/s | Notes | |---|---|---|---| | Arrow Full Scan | 15.5M | 355.4 | executeQueryToArrow + tableFromIPC — fastest | | Arrow Filtered | 15.6M | 358.1 | Arrow IPC with WHERE sensor_id = 5 | | Columnar Full Scan | 7.9M | 180.9 | executeQueryColumnar — no Arrow dependency | | Columnar Filtered | 5.5M | 125.3 | Columnar stream with WHERE filter | | Full Scan (eager) | 1.0M | 22.8 | executeQuery — all rows in memory | | Full Scan (stream) | 836K | 19.1 | executeQueryStream — row-level iteration | | Full Scan (chunked) | 637K | 14.6 | nextChunk() loop with per-row access | | Aggregation | 195M | 4464.8 | GROUP BY — server-side, 10 result rows |

Insert Performance

| Benchmark | Rows/sec | MB/s | Notes | |---|---|---|---| | Insert (COPY) | 1.4M | 31.2 | Inserter.addRows() batched in 50K chunks |

Which Query API to Use

| API | Speed | Memory | Best for | |---|---|---|---| | executeQueryToArrow | Fastest | All in memory | Analytics, export, Arrow ecosystem | | executeQueryColumnar | Fast | One chunk at a time | Streaming numeric processing | | executeQuery | Moderate | All in memory | Small results, simple access | | executeQueryStream | Moderate | One chunk at a time | Large results, row-level logic |

Examples

The examples/ directory contains runnable examples:

| Example | Description | Run | |---|---|---| | complete-api-tour.mts | Full 19-section tour in TypeScript | npx tsx examples/complete-api-tour.mts | | complete-api-tour.mjs | Same tour in plain JavaScript | node examples/complete-api-tour.mjs | | typed-analytics.mts | TypeScript analytics pipeline | npx tsx examples/typed-analytics.mts | | arrow-analytics.mjs | Arrow integration deep-dive | node examples/arrow-analytics.mjs | | hyper-explorer/ | Web-based database inspector & generator | See below |

All examples require HYPERD_PATH to be set.

Hyper Explorer

A full-stack web application (React + Express) for inspecting and generating .hyper files: schema browser, data preview, column analytics, SQL editor, database generator, and drag-and-drop file loading. See the Hyper Explorer README for setup and full details.

Documentation

| Document | Description | |----------|-------------| | DEVELOPMENT.md | Building from source, testing, publishing, contributing | | Node.js API Summary | One-page overview: architecture, query API tiers, Arrow integration | | Hyper Explorer README | Architecture and API for the web-based database inspector |

License

Apache-2.0