Skip to main content

Node.js API Reference

Complete reference for the vectlite npm package.

const vectlite = require('vectlite')
// or
import * as vectlite from 'vectlite'

Module Functions​

open​

function open(path: string, options?: OpenOptions): Database

Open or create a vectlite database.

ParameterTypeDescription
pathstringPath to the .vdb file. Created if it does not exist.
optionsOpenOptionsOptional configuration.

OpenOptions

PropertyTypeDefaultDescription
dimensionnumber--Vector dimension. Required when creating a new database.
metricstring'cosine'Distance metric used at creation time. One of 'cosine', 'euclidean' (alias 'l2'), 'dotproduct' (aliases 'dot', 'ip', 'inner_product', 'dot_product'), or 'manhattan' (alias 'l1'). Persisted in the database file. Ignored when opening an existing database. Scores are always oriented so higher is better.
readOnlybooleanfalseOpen in read-only mode.
lockTimeoutnumber--Maximum seconds to wait when acquiring the advisory file lock. Omit to fail immediately on contention.

Returns: Database


openStore​

function openStore(root: string): Store

Open or create a collection store directory.

ParameterTypeDescription
rootstringPath to the store directory.

Returns: Store


restore​

function restore(source: string, dest: string): Database

Restore a backup to a new database path.

ParameterTypeDescription
sourcestringPath to the backup directory.
deststringPath for the restored .vdb file.

Returns: Database


sparseTerms​

function sparseTerms(text: string): SparseVector

Tokenize and weight text into a sparse term vector for BM25 search.

ParameterTypeDescription
textstringInput text.

Returns: SparseVector (Record<string, number>)


upsertText​

function upsertText(
db: Database,
id: string,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
metadata?: Metadata,
options?: { namespace?: string }
): void | Promise<void>

High-level helper that generates a dense embedding and sparse terms from text, then upserts the record. If embed returns a Promise, the function is async.

ParameterTypeDescription
dbDatabaseTarget database.
idstringRecord identifier.
textstringText to embed and index.
embed(text: string) => number[] | Promise<number[]>Embedding function.
metadataMetadataOptional metadata object.
options.namespacestringOptional namespace.

searchText​

function searchText(
db: Database,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
options?: SearchOptions
): SearchResult[] | Promise<SearchResult[]>

High-level hybrid search. Generates a dense embedding and sparse terms from text, then runs a fused search. Async if embed returns a Promise.

ParameterTypeDescription
dbDatabaseTarget database.
textstringNatural-language query.
embed(text: string) => number[] | Promise<number[]>Embedding function.
optionsSearchOptionsSearch configuration (see SearchOptions).

Returns: SearchResult[]


searchTextWithStats​

function searchTextWithStats(
db: Database,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
options?: SearchOptions
): SearchResponse | Promise<SearchResponse>

Same as searchText but returns a SearchResponse with results and query statistics.


configureOpenTelemetry​

function configureOpenTelemetry(
config?: boolean | {
tracer?: unknown
tracerName?: string
enabled?: boolean
}
): unknown

Enable, disable, or configure optional OpenTelemetry tracing for search operations. When active, every searchText() and searchTextWithStats() call is wrapped in a vectlite.search span carrying db.system, db.operation.name, and search-specific attributes (vectlite.search.k, .namespace, .has_dense, .has_sparse, .fusion, .used_ann, .result_count, .total_us).

@opentelemetry/api is loaded lazily and is not a runtime dependency. If a search throws, the span records the exception and sets an error status before re-throwing.

FormBehaviour
configureOpenTelemetry()Auto-detect: resolves a tracer from @opentelemetry/api if installed.
configureOpenTelemetry({ tracer: myTracer })Use a user-supplied tracer instance.
configureOpenTelemetry({ tracerName: 'my-app' })Override the tracer name (default 'vectlite').
configureOpenTelemetry({ enabled: false }) or configureOpenTelemetry(false)Disable tracing.

Returns: the active tracer (or null when disabled).


Database​

Returned by open() and restore().

Properties​

PropertyTypeDescription
pathstringAbsolute path to the .vdb file.
walPathstringPath to the write-ahead log file.
dimensionnumberVector dimension.
metricstringDistance metric: 'cosine', 'euclidean', 'dotproduct', or 'manhattan'.
readOnlybooleanWhether opened in read-only mode.
quantizationMethodstring | nullActive quantization method ('scalar', 'binary', 'product') or null.
Property vs method

quantizationMethod is a property. Use db.isQuantized() as a method to check whether quantization is enabled.

count​

count(options?: { namespace?: string; filter?: Filter }): number

Return the number of records, optionally scoped by namespace and/or filter.

namespaces​

namespaces(): string[]

Return all namespace names.

transaction​

transaction(): Transaction

Begin a new transaction.

Returns: Transaction

insert​

insert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void

Insert a new record. Throws VectLiteError if the ID already exists.

upsert​

upsert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void

Insert or replace a record.

WriteOptions

PropertyTypeDescription
namespacestringTarget namespace.
sparseSparseVectorSparse term vector for BM25.
vectorsNamedVectorsAdditional named vectors.
ttlnumberTime-to-live in seconds. Expired records are filtered from reads and garbage-collected on compact().

insertMany​

insertMany(records: Record[], options?: { namespace?: string }): number

Batch insert. Returns the number of records inserted.

upsertMany​

upsertMany(records: Record[], options?: { namespace?: string }): number

Batch upsert. Returns the number of records upserted.

bulkIngest​

bulkIngest(
records: Record[],
options?: BulkIngestOptions
): number

Ingest a batch of records efficiently.

Since 0.10.0, bulkIngest uses parallel HNSW insertion (Rayon-backed) when the dataset is large enough (>= parallelInsertThreshold, default 256) and coalesces WAL writes into a single fsync at the end of the batch — typically 10–20× faster than the previous fsync-per-batch path.

Use bulkIngestArray for large batches

For large batches, prefer bulkIngestArray — it takes a Float32Array directly (zero-copy via napi-rs) and skips the JSON-serialise round-trip the legacy bulkIngest(records) path performs. Typically 10–30× faster on large batches.

BulkIngestOptions

PropertyTypeDefaultDescription
namespacestring--Target namespace.
batchSizenumber10000Internal WAL batch size.
mnumber--HNSW out-degree per node. Higher = better recall, larger graph. (0.10.0+)
efConstructionnumber--HNSW build-time search width. Higher = better recall, slower ingest. (0.10.0+)
efSearchnumber--HNSW query-time search width. Free to retune. (0.10.0+)
parallelInsertThresholdnumber256Dataset size at which parallel insertion kicks in. (0.10.0+)
tombstoneRebuildPctnumber30HNSW rebuild threshold after deletes. (0.11.0+)
segmentSizeThresholdnumber50000HNSW segment size in LSM-style segmenting. (0.12.0+)

Returns: number -- total records ingested.

See: HNSW tuning guide, and db.setIndexConfig() below for retuning an existing database.

bulkIngestArray​

Added in 0.12.0.

bulkIngestArray(
ids: string[],
vectorsFlat: Float32Array,
dim: number,
options?: BulkIngestArrayOptions
): number

Zero-copy bulk ingest from a flat Float32Array. napi-rs hands the Rust core a reference into the underlying ArrayBuffer, so no JSON serialisation happens between JS and Rust. 10–30× faster than bulkIngest(records) on large batches.

const ids = []
const flat = new Float32Array(N * 384) // row-major

for (let i = 0; i < N; i++) {
ids.push(`doc-${i}`)
flat.set(embeddings[i], i * 384)
}

db.bulkIngestArray(ids, flat, 384, { namespace: 'docs' })

BulkIngestArrayOptions

Accepts the same fields as BulkIngestOptions plus an optional metadata?: Metadata[] array parallel to ids.

PropertyTypeDefaultDescription
metadataMetadata[]--Optional metadata array, parallel to ids.
namespace, batchSize, m, efConstruction, efSearch, parallelInsertThreshold, tombstoneRebuildPct, segmentSizeThresholdSame semantics as BulkIngestOptions.

Returns: number — total records ingested.

annSegmentCount​

Added in 0.12.0.

annSegmentCount(namespace?: string, vectorName?: string): number

Return the number of HNSW segments in the active ANN index. Since 0.12.0, the HNSW index is built LSM-tree style: new inserts land in the active segment until it hits segmentSizeThreshold (default 50000), then a fresh segment is started. Search queries every segment and merges top-K.

Older ANN1 / ANN2 manifests still load — they report as 1-segment indexes.

get​

get(id: string, options?: { namespace?: string }): Record | null

Retrieve a record by ID. Returns null if not found.

delete​

delete(id: string, options?: { namespace?: string }): boolean

Delete a record. Returns true if the record existed.

deleteMany​

deleteMany(ids: string[], options?: { namespace?: string }): number

Delete multiple records. Returns the count of records deleted.

deleteByFilter​

deleteByFilter(filter: Filter, options?: { namespace?: string }): number

Delete every record that matches filter in a single pass. Returns the number of records deleted.

const deleted = db.deleteByFilter({ stale: true }, { namespace: 'docs' })

updateMetadata​

updateMetadata(
id: string,
metadata: Metadata,
options?: { namespace?: string }
): boolean

Merge a metadata patch into an existing record without re-writing the vector. Keys present in metadata overwrite existing keys; other keys are left untouched. Skips ANN, sparse, quantized, and multi-vector index rebuilds when a WAL batch contains only metadata updates.

Returns true if the record was found and updated.

db.updateMetadata('doc1', { status: 'reviewed', score: 0.95 })

list​

list(options?: {
namespace?: string
filter?: Filter
limit?: number // 0 = no limit
offset?: number
}): Record[]

List records without issuing a vector query.

listCursor​

listCursor(options?: {
namespace?: string
filter?: Filter
limit?: number // default: 100
cursor?: string | null
}): { records: Record[]; cursor: string | null }

Cursor-based pagination for efficient iteration over large collections. cursor is null when the iterator is exhausted.

let cursor = null
do {
const page = db.listCursor({ limit: 100, cursor })
for (const record of page.records) process(record)
cursor = page.cursor
} while (cursor)

setTtl​

setTtl(
id: string,
ttlSecs: number,
options?: { namespace?: string }
): boolean

Set a time-to-live (in seconds from now) on an existing record. Expired records are transparently filtered from get(), list(), count(), and search(), and are permanently removed on compact(). The expiresAt field on Record exposes the absolute expiry timestamp (epoch seconds).

clearTtl​

clearTtl(id: string, options?: { namespace?: string }): boolean

Remove the expiry from a record so it lives indefinitely.

createIndex​

createIndex(field: string, indexType: 'keyword' | 'numeric'): void

Create a payload index on a metadata field to accelerate filtered queries 10-100x on large collections. Indexes are automatically used by search(), count(), and list() to narrow candidates before full filter evaluation. AND filters intersect index results; OR filters union when all sub-filters are indexed. Index definitions persist across close/reopen in a .vdb.pidx sidecar file.

db.createIndex('source', 'keyword')   // string equality, $in
db.createIndex('score', 'numeric') // range queries: $gt, $gte, $lt, $lte

dropIndex​

dropIndex(field: string): boolean

Remove a payload index. Returns true if the index existed.

listIndexes​

listIndexes(): Array<[string, string]>

Return all active payload indexes as [field, indexType] tuples.

enableQuantization​

enableQuantization(
method: 'scalar' | 'binary' | 'product' | 'pq',
options?: {
rescoreMultiplier?: number // default: 4 — fetches k × rescoreMultiplier candidates
numSubVectors?: number // PQ only; omit for a dimension-aware default
numCentroids?: number // PQ only, default: 256
}
): void

Enable vector quantization to reduce memory and accelerate search. All methods use a 2-stage pipeline: fast quantized candidate selection followed by exact float32 rescoring. Parameters persist in a .vdb.quant sidecar file and reload automatically on open. Indexes rebuild automatically on inserts, upserts, and bulk ingestion. Method names are parsed case-insensitively.

MethodCompressionNotes
scalar~4x (int8)Minimal recall loss.
binary~32xBest for normalized embeddings (Hamming distance filtering).
product / pqconfigurablek-means PQ for very large datasets.
db.enableQuantization('scalar')
db.enableQuantization('binary', { rescoreMultiplier: 10 })
db.enableQuantization('pq') // dimension-aware default for numSubVectors
db.enableQuantization('product', { numSubVectors: 16, numCentroids: 256 })

disableQuantization​

disableQuantization(): void

Disable quantization and remove the persisted .vdb.quant sidecar.

isQuantized​

isQuantized(): boolean

Whether vector quantization is currently enabled. Method, not a property since 0.9.1.

validNumSubVectors​

validNumSubVectors(): number[]

Return the list of valid numSubVectors values for this database's dimension (i.e. the divisors that allow an even split). Useful before calling enableQuantization('pq', { numSubVectors: ... }).

upsertMultiVectors​

upsertMultiVectors(
id: string,
vector: number[],
multiVectors: Record<string, number[][]>,
metadata?: Metadata,
options?: { namespace?: string; sparse?: SparseVector }
): void

Insert or replace a record carrying per-token (ColBERT, ColPali) embeddings for late-interaction search. vector is the usual dense embedding; multiVectors maps space names to a list of token vectors.

db.upsertMultiVectors(
'doc1',
denseVector,
{ colbert: [tokenVec1, tokenVec2 /* ... */] },
{ source: 'paper' }
)

searchMultiVector​

searchMultiVector(
space: string,
queryTokens: number[][],
options?: {
k?: number
namespace?: string
filter?: Filter
}
): SearchResult[]

MaxSim late-interaction search: for each query token, take the max cosine similarity against all document tokens, then sum across query tokens.

enableMultiVectorQuantization​

enableMultiVectorQuantization(space: string): void

Enable ColBERTv2-style 2-bit quantization (~16x compression) for a multi-vector space. Parameters persist in a .vdb.mvquant.<space> sidecar file.

disableMultiVectorQuantization​

disableMultiVectorQuantization(space: string): void

Disable 2-bit quantization for a multi-vector space.

isMultiVectorQuantized​

isMultiVectorQuantized(space: string): boolean

Whether a multi-vector space currently has 2-bit quantization enabled.

close​

close(): void

Flush pending state, release the file lock, and invalidate the handle. Subsequent operations that return data throw VectLiteError.

Async methods​

Non-blocking versions of heavy operations, scheduled on the libuv threadpool via napi::Task (no tokio dependency). Use them in hot HTTP paths to avoid stalling the event loop.

db.searchAsync(vector: number[] | null, options?: SearchOptions): Promise<SearchResult[]>
db.searchWithStatsAsync(vector: number[] | null, options?: SearchOptions): Promise<SearchResponse>
db.bulkIngestAsync(records: Record[], options?: BulkIngestOptions): Promise<number>
db.flushAsync(): Promise<void>
db.compactAsync(): Promise<void>
const results = await db.searchAsync(queryVector, { k: 10, filter: { source: 'blog' } })

flush​

flush(): void

Alias for compact() in the current package.

compact​

compact(): void

Merge WAL into the main .vdb file and rebuild ANN indexes as needed.

snapshot​

snapshot(dest: string): void

Create a self-contained database copy at dest.

backup​

backup(dest: string): void

Full backup of .vdb and ANN sidecar files to dest directory.

indexConfig​

Added in 0.10.0.

indexConfig(): {
m: number
efConstruction: number
efSearch: number
parallelInsertThreshold: number
tombstoneRebuildPct: number // since 0.11.0
segmentSizeThreshold: number // since 0.12.0
}

Return the current HNSW tuning parameters.

setIndexConfig​

Added in 0.10.0.

setIndexConfig(options: {
m?: number
efConstruction?: number
efSearch?: number
parallelInsertThreshold?: number
tombstoneRebuildPct?: number // since 0.11.0
segmentSizeThreshold?: number // since 0.12.0
}): void

Retune the HNSW configuration on an existing database. Changing m or efConstruction triggers a full ANN rebuild. Changing only efSearch is free (query-time only) and recommended as the first knob to try when tuning recall vs latency. segmentSizeThreshold (0.12.0+) controls when a fresh HNSW segment is started during streaming inserts.

db.setIndexConfig({ efSearch: 128 })  // higher recall, slightly slower

setEfSearch​

Added in 0.10.0.

setEfSearch(value: number | null): void

Shortcut to retune only the query-time efSearch. Pass null to fall back to the engine default.

setWalSyncMode​

Added in 0.11.0.

setWalSyncMode(mode: 'per_op' | 'every_n' | 'on_flush', n?: number): void

Choose the WAL durability strategy. Trades a bounded amount of recently-acked data on crash for 5–10× higher single-record ingestion throughput on macOS APFS.

modeBehaviour
'per_op'(Default) fsync after every insert. Strongest durability.
'every_n'fsync every n inserts (requires n). Amortises the fsync tax.
'on_flush'fsync only at flush / compact / close. Highest throughput.
db.setWalSyncMode('every_n', 64)
// ...
db.setWalSyncMode('on_flush')

walSyncMode​

Added in 0.11.0.

walSyncMode(): WalSyncModeInfo

Return the current WAL sync mode. WalSyncModeInfo is { mode: 'per_op' } | { mode: 'every_n', n: number } | { mode: 'on_flush' }.

tombstoneStats​

Added in 0.11.0.

tombstoneStats(): { live: number; dead: number }

Return live and tombstoned counts across HNSW graphs. Since 0.11.0, delete marks records as tombstoned rather than rebuilding the graph. The graph is rebuilt at compact() time, or whenever the dead/live ratio exceeds tombstoneRebuildPct (default 30%).

prepareForScan​

Added in 0.11.0.

prepareForScan(): void

Materialise the contiguous dense-vector arena up front. Useful before a heavy brute-force / rescoring workload — avoids the first-call latency spike when the arena is built lazily.

vectorArenaLen​

Added in 0.11.0.

vectorArenaLen(): number | null

Return the number of vectors currently held in the contiguous arena, or null if the arena is not materialised.

search(
vector: number[] | null,
options?: SearchOptions
): SearchResult[]

// Shorthand: second arg is k (since 0.9.2)
search(vector: number[] | null, k: number): SearchResult[]

// Single-object form (since 0.9.1)
search(options: SearchOptions & { query: number[] | null }): SearchResult[]

Run a search query. Pass null as the vector for sparse-only or multi-vector search.

// Positional form
db.search(queryVector, { k: 10, filter: { source: 'blog' } })

// Shorthand — second arg is k (since 0.9.2)
db.search(queryVector, 10)

// Single-object form (since 0.9.1)
db.search({ query: queryVector, k: 10, filter: { source: 'blog' } })

All three forms are also supported by searchWithStats, searchAsync, and searchWithStatsAsync.

Strict dimension matching (changed in 0.9.1)

Search now rejects query vectors whose length does not match the database dimension, returning DimensionMismatch. Previously, undersized queries were silently truncated via Matryoshka logic. To opt into prefix search, pass truncateDim in the options. An all-zeros vector also raises an error for cosine / dot-product metrics.

Returns: SearchResult[]

searchWithStats​

searchWithStats(
vector: number[] | null,
options?: SearchOptions
): SearchResponse

Same as search() but returns a SearchResponse.


Store​

Returned by openStore(). Manages a directory of collections.

Properties​

PropertyTypeDescription
rootstringAbsolute path to the store directory.

collections​

collections(): string[]

List all collection names.

createCollection​

createCollection(name: string, dimension: number): Database

Create a new collection. Throws if it already exists.

openOrCreateCollection​

openOrCreateCollection(name: string, dimension: number): Database

Open or create a collection.

openCollection​

openCollection(name: string): Database

Open an existing collection. Throws if it does not exist.

dropCollection​

dropCollection(name: string): boolean

Delete a collection and its data from disk. Returns true if it existed.

close​

close(): void

Symmetry with Database.close(). The store holds no open file handles, so this is currently a no-op, but using it future-proofs your code across binding versions. Available since 0.9.1.


Transaction​

Returned by Database.transaction(). Node.js does not have a with statement, so you must call commit() or rollback() manually.

const tx = db.transaction()
try {
tx.upsert('doc1', emb1, { source: 'a' })
tx.delete('old_doc')
tx.commit()
} catch (err) {
tx.rollback()
throw err
}

insert​

insert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void

upsert​

upsert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void

insertMany​

insertMany(records: Record[], options?: { namespace?: string }): number

upsertMany​

upsertMany(records: Record[], options?: { namespace?: string }): number

delete​

delete(id: string, options?: { namespace?: string }): boolean

commit​

commit(): void

Commit all queued operations atomically.

rollback​

rollback(): void

Discard all queued operations.

count​

count(): number

Number of queued operations in the transaction.


Interfaces​

OpenOptions​

interface OpenOptions {
dimension?: number
readOnly?: boolean
}

WriteOptions​

interface WriteOptions {
namespace?: string
sparse?: SparseVector
vectors?: NamedVectors
}

SearchOptions​

interface SearchOptions {
k?: number // default: 10
filter?: Filter
namespace?: string
allNamespaces?: boolean // default: false
sparse?: SparseVector
denseWeight?: number // default: 1.0
sparseWeight?: number // default: 1.0
fetchK?: number // default: 0
mmrLambda?: number
vectorName?: string
fusion?: 'linear' | 'rrf' // default: 'linear'
rrfK?: number // default: 60
queryVectors?: NamedVectors
vectorWeights?: Record<string, number>
explain?: boolean // default: false
truncateDim?: number // Matryoshka prefix search
}

truncateDim enables Matryoshka prefix search: query and database vectors are truncated to the first truncateDim dimensions before scoring. Required when the query length is shorter than the database dimension; otherwise the engine throws DimensionMismatch.

BulkIngestOptions​

interface BulkIngestOptions {
namespace?: string
batchSize?: number // default: 10000
}

Record​

interface Record {
namespace: string
id: string
vector: number[]
vectors: NamedVectors
multiVectors?: Record<string, number[][]>
sparse: SparseVector
metadata: Metadata
expiresAt?: number | null // epoch seconds (TTL)
}

expiresAt is the absolute expiry timestamp in epoch seconds when a TTL is set (otherwise null).

SearchResult​

interface SearchResult {
namespace: string
id: string
score: number
dense_score: number
sparse_score: number
vector_name: string | null
matched_terms: string[]
dense_rank: number | null
sparse_rank: number | null
metadata: Metadata
bm25_term_scores: Record<string, number>
explain?: ExplainDetails
}

ExplainDetails​

interface ExplainDetails {
fusion?: string
dense_score?: number
sparse_score?: number
matched_terms?: string[]
vector_name?: string | null
dense_rank?: number | null
sparse_rank?: number | null
bm25_term_scores?: Record<string, number>
}

SearchStats​

interface SearchStats {
used_ann: boolean
ann_candidate_count: number
exact_fallback: boolean
considered_count: number
fetch_k: number
mmr_applied: boolean
sparse_candidate_count: number
ann_loaded_from_disk: boolean
wal_entries_replayed: number
fusion: string
rerank_applied: boolean
rerank_count: number
timings: SearchTimings
}

SearchTimings​

interface SearchTimings {
dense_us: number
sparse_us: number
fusion_us: number
total_us: number
}

SearchResponse​

interface SearchResponse {
results: SearchResult[]
stats: SearchStats
}

MetadataValue​

type MetadataValue = string | number | boolean | null | MetadataValue[] | { [key: string]: MetadataValue }

Metadata​

type Metadata = Record<string, MetadataValue>

SparseVector​

type SparseVector = Record<string, number>

NamedVectors​

type NamedVectors = Record<string, number[]>

Filter​

type Filter = Record<string, any>

MongoDB-style filter expression. See Metadata Filters for the full query syntax.


Error​

VectLiteError​

class VectLiteError extends Error {
constructor(message: string)
}

Thrown for all vectlite errors including:

  • Write operations on a read-only database
  • Duplicate ID on insert()
  • Dimension mismatch
  • Corrupt database file
  • File lock contention
  • I/O errors