HBaseStore — Apache HBase wide-column store

Read this page in the documentation

HBaseStore — Apache HBase wide-column store Overview Apache HBase is a wide-column, sorted-map distributed store built on top of HDFS, modelled on Google's Bigtable paper. Data is addressed by a (row key, column family, column qualifier, timestamp) tuple: rows are identified by an arbitrary byte-string row key (kept in sorted order), and each row holds cells grouped into a small, fixed set of column families, with an unbounded number of qualifiers per family created on the fly at write time. There is no fixed schema beyond the family list, no joins, no secondary indexes, and no arbitrary WHERE filtering — reads are either single-row gets by row key or range scans over the sorted key space. None of that fits the SQL-shaped Dialect interface, so HBaseStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes HBase's own row/cell operations directly. Column cells are addressed as 'family:qualifier', matching HBase's own column naming. Identity: Property | Value | --------- | --------- | name | 'hbase' | library | 'hbase' | The store is built on the hbase npm package, an HBase Stargate / REST-gateway client. That driver exposes a fluent surface — client.table(name).row(key).put(...), client.table(name).scan(...) — with Node-style (err, result) callbacks. The store assumes that shape and promisifies each call. Lazy loading — not a hard dependency hbase is an optional peer dependency, not a hard dependency of this package. It is loaded lazily via require('hbase') inside connect(), rather than a top-level import. Importing this module therefore never forces the driver to be installed — it is only needed when the store actually builds its own client. Injected client HBaseStoreOptions accepts a pre-built hbase client (or a compatible mock) via client. When provided, connect() uses it directly and skips require('hbase') entirely. This is how the test suite injects a mock client (no driver, no network) and how callers can supply a custom-configured client. Connection Build a store from connection options and call connect(): The hbase REST client is stateless (each call is an HTTP request), so connect() builds the client but opens no persistent socket. All connection options are optional: Option | Type | Purpose | --------------- | ------------------------- | --------------------------------------------------------------------------------- | host | string | REST/Stargate gateway host, e.g. '127.0.0.1'. | port | number | REST/Stargate gateway port, e.g. 8080. | clientOptions | Record<string, unknown> | Escape hatch for any other hbase client option (protocol, encoding, auth, timeout, …). | client | HBaseClientLike | A pre-built client (mock or custom config). When set, the driver is not required. | Injected-client form Supply your own client (or a mock) to bypass driver-based client construction: Methods Driver failures are wrapped in a DatabaseError; using any method before connect() (or after disconnect()) throws a ConnectionError. Each row/cell method promisifies the driver's callback-style call. Lifecycle Method | Signature | Behavior | -------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires hbase and builds a client from the options. Idempotent; wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | The REST client is stateless (no socket), so this drops the client reference and flips connected to false. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): HBaseClientLike | Returns the underlying hbase client for anything not wrapped here. Throws ConnectionError if not connected. | Row / cell operations Method | Signature | Behavior | ------------ | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | put | put(table: string, row: string, data: HBaseRowData): Promise<unknown> | Writes one or more cells into a single row. data maps 'family:qualifier' keys to values; passed to the driver as parallel columns/values arrays via table(table).row(row).put(...). Throws DatabaseError if data is empty. | get | get(table: string, row: string): Promise<unknown> | Reads a single row by key via table(table).row(row).get(...), returning the row's cells as the driver surfaces them (e.g. an array of { column, timestamp, $ } cell descriptors). | deleteRow | deleteRow(table: string, row: string): Promise<unknown> | Deletes an entire row (all its cells) via table(table).row(row).delete(...). | scan | scan(table: string, opts?: HBaseScanOptions): Promise<unknown> | Range-scans a table via table(table).scan(options, ...), returning the matching rows. startRow/endRow define a contiguous range over the sorted key space; columns restricts which families/qualifiers are read. opts defaults to {}. | Table lifecycle Method | Signature | Behavior | ------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | createTable | createTable(table: string, families: string[]): Promise<unknown> | Creates a table with the given column families via table(table).create(...). Each family is sent as an HBase ColumnSchema entry ({ ColumnSchema: [{ name }, …] }). Throws DatabaseError if families is empty. Families must be declared up front — unlike qualifiers, they cannot be added implicitly at write time. | Supporting types Example Verification status Unit / mock-verified only. The tests in tests/nosql/hbase.test.ts are fully mock-driven: a fake client with the fluent table()/row() surface (exposing put/get/delete/scan/create jest spies backed by Node-style callbacks) is injected via HBaseStoreOptions.client. That option makes connect() skip require('hbase') entirely, so the tests exercise the real store code paths with no driver installed and no network. What this proves: name/library identity, and the connection lifecycle (isConnected() after connect(), idempotent second connect() keeping the same client, clean disconnect()). put() routing to table(t).row(r).put(columns, values) with parallel arrays and throwing for empty data; get() and deleteRow() routing to the right row(key) handle; scan() forwarding opts (defaulting to {}); createTable() building the ColumnSchema from families and throwing for an empty list. Error handling: driver callback errors wrapped in DatabaseError, and ConnectionError from getClient()/put() before connect() and after disconnect(). What this does not prove: live execution against a real HBase / Stargate REST gateway. Call routing and payload shapes are verified against the driver's assumed contract, but end-to-end execution over the wire has not been exercised here. Related reading All data stores — the full catalogue, grouped by purpose Database types — where this sits among the 22 categories