BigtableStore — Google Cloud Bigtable wide-column store

Read this page in the documentation

BigtableStore — Google Cloud Bigtable wide-column store Overview Google Cloud Bigtable is Google's fully-managed wide-column store — the system the original Bigtable paper describes and that HBase was later modelled on. Data is a sparse, sorted map addressed by (row key, column family, column qualifier, timestamp): rows are keyed by an arbitrary byte-string row key (stored in lexicographic order), grouped into a small set of column families, with an unbounded number of qualifiers per family. There is no fixed schema beyond the family list, no joins, no secondary indexes, and no arbitrary WHERE filtering — reads are single-row lookups by key or range/filter scans over the sorted key space. None of that fits the SQL-shaped Dialect interface, so BigtableStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes Bigtable's own row operations directly. Identity: Property | Value | --------- | ------------------------- | name | 'bigtable' | library | '@google-cloud/bigtable'| The store is built on the official @google-cloud/bigtable client, which is resource-oriented: a Bigtable instance resolves to an instance(id), which resolves to a table(id), on which insert/getRows/row(key)/create operate. The store caches the resolved instance and table handles (per table id) and exposes flat insertRow/getRow/deleteRow/readRows/createTable methods over them. Lazy loading — not a hard dependency @google-cloud/bigtable is an optional peer dependency, not a hard dependency of this package. It is loaded lazily via require('@google-cloud/bigtable') 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 BigtableStoreOptions accepts a pre-built Bigtable client (or a compatible mock) via client. When provided, connect() uses it directly and skips require('@google-cloud/bigtable') 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(): instanceId and tableId are configurable defaults — every row method also accepts an explicit table name to target a different table within the same instance. connect() resolves the instance handle once (when instanceId is set); table handles are resolved lazily and cached per table id. All connection options are optional: Option | Type | Purpose | --------------- | ------------------------- | --------------------------------------------------------------------------------------------- | projectId | string | GCP project id that owns the Bigtable instance. | instanceId | string | Bigtable instance id, used as the default for all table operations. | tableId | string | Default table id, used when a method's table argument is omitted. | clientOptions | Record<string, unknown> | Escape hatch for any other Bigtable constructor option (keyFilename, credentials, apiEndpoint, …). | client | BigtableClientLike | 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. Resolving a table requires both an instance id and a table id — a missing instanceId or unresolvable tableId throws a DatabaseError. Lifecycle Method | Signature | Behavior | -------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires @google-cloud/bigtable and builds a Bigtable client. Resolves the instance handle once when instanceId is set. Idempotent; wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | The client manages its own gRPC channel pool (no public close on the surface used here), so this drops the cached handles and flips connected to false. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): BigtableClientLike | Returns the underlying Bigtable client for anything not wrapped here. Throws ConnectionError if not connected. | Row operations Method | Signature | Behavior | ------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | insertRow | insertRow(table: string, key: string, data: BigtableRowData): Promise<unknown> | Inserts (writes) a single row via table.insert([{ key, data }]). data maps column families to { qualifier: value } maps, e.g. { cf: { name: 'alice', age: 30 } }. | getRow | getRow(table: string, key: string): Promise<unknown> | Reads a single row by key via table.row(key).get(), returning the row data as the driver surfaces it. | deleteRow | deleteRow(table: string, key: string): Promise<unknown> | Deletes an entire row (all its cells) via table.row(key).delete(). | readRows | readRows(table: string, opts?: BigtableReadOptions): Promise<unknown> | Reads multiple rows via table.getRows(options). opts (ranges, keys, prefix, limit, filter) is forwarded straight to the client; a ranges/prefix scan reads a contiguous slice of the sorted key space. 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.create({ families }). 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/bigtable.test.ts are fully mock-driven: a fake client with the resource-oriented instance().table() chain (exposing insert/getRows/row().get/row().delete/create jest spies) is injected via BigtableStoreOptions.client. That option makes connect() skip require('@google-cloud/bigtable') 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 (resolving the instance via client.instance('my-instance') on connect(), idempotent second connect(), clean disconnect(), isConnected() transitions). insertRow() routing to table.insert([{ key, data }]); getRow()/deleteRow() routing through table.row(key); readRows() forwarding opts (defaulting to {}); createTable() routing to table.create({ families }) and throwing for an empty list. Table resolution: throwing DatabaseError when no instanceId is configured or no tableId can be resolved, and caching the resolved table handle across calls (instance().table() resolved once). Error handling: driver failures wrapped in DatabaseError, and ConnectionError from getClient()/insertRow() before connect() and after disconnect(). What this does not prove: live execution against real Google Cloud Bigtable (or the emulator). Call routing and payload shapes are verified against the client's documented 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