TypesenseStore — Typesense search engine
Read this page in the documentation
TypesenseStore — Typesense search engine Overview Typesense is a typo-tolerant search engine built around an inverted index with faceting, filtering and ranking rather than SQL: no JOIN, no transaction log, documents grouped into strongly-typed collections (each described by a schema of fields). Because none of that fits the SQL-shaped Dialect interface, TypesenseStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes Typesense's real capabilities grouped by concern: connection lifecycle, collection management, and document CRUD/search. Operations are reached through the driver's fluent, resource-scoped API: client.collections() / client.collections(name) for collections and client.collections(name).documents() / .documents(id) for documents. Identity: Property | Value | --------- | ------------- | name | 'typesense' | library | 'typesense' | Lazy loading — not a hard dependency The official typesense JS driver is not a hard dependency of this package. It is loaded lazily via require('typesense') inside connect(), rather than a top-level import. Importing this module therefore does not force the dependency to be resolved unless a Typesense store is actually constructed and connected. Injected client TypesenseStoreOptions accepts a pre-built client (or a compatible mock) via client. When provided, connect() uses it as-is instead of instantiating a new one (and nodes/apiKey are ignored). This is how the test suite injects a mock client (no driver, no live server) and how callers can take full control over driver configuration. Connection Build a store from connection options and call connect(): connect() runs a health.retrieve() check to surface connection failures (bad host, refused connection) immediately rather than on the first real operation. A refused connection (ECONNREFUSED) is reported as a ConnectionError with the message 'Connection refused'. Option | Type | Purpose | -------- | ----------------- | --------------------------------------------------------------------------------------------- | nodes | TypesenseNode[] | One or more Typesense nodes to connect to ({ host, port, protocol }). | apiKey | string | API key used to authenticate against the server. | client | any | An already-built client to use instead of building one. When set, nodes/apiKey are ignored. | The options type also has an index signature ([key: string]: unknown) so additional options can be passed through. Injected-client form Supply your own client (or a mock) to bypass driver-based client construction: Methods Every operation is dispatched through an internal exec helper that requires a connected client and wraps driver failures in a DatabaseError (including the HTTP status when the driver surfaces one). Using getClient() before connect() throws a ConnectionError. Lifecycle Method | Signature | Behavior | -------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | connect | connect(): Promise<void> | Uses an injected client if provided, otherwise lazy-requires typesense and builds a Typesense.Client from nodes/apiKey, then runs client.health.retrieve(). Idempotent; wraps failures in ConnectionError. | disconnect | disconnect(): Promise<void> | The client is stateless HTTP (no socket to close), so this clears the client reference and connection state. | isConnected | isConnected(): boolean | true only when connected and a client is present. | getClient | getClient(): any | Returns the underlying typesense client for anything not wrapped here. Throws ConnectionError if not connected. | Collection management Method | Signature | Behavior | ------------------ | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | createCollection | createCollection(schema: Record<string, unknown>): Promise<any> | Creates a collection from a schema ({ name, fields, defaultsortingfield? }) via client.collections().create(schema). | deleteCollection | deleteCollection(name: string): Promise<any> | Deletes a collection by name via client.collections(name).delete(). | Document CRUD + search Method | Signature | Behavior | ---------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | indexDocument | indexDocument(collection: string, document: Record<string, unknown>): Promise<any> | Indexes (creates) a single document via client.collections(collection).documents().create(document). | search | search<T = Record<string, unknown>>(collection: string, params: Record<string, unknown>): Promise<T> | Runs a search via client.collections(collection).documents().search(params). Returns the full search response. | getDocument | getDocument<T = Record<string, unknown>>(collection: string, id: string): Promise<T> | Retrieves a single document by id via client.collections(collection).documents(id).retrieve(). | deleteDocument | deleteDocument(collection: string, id: string): Promise<any> | Deletes a single document by id via client.collections(collection).documents(id).delete(). | Example Verification status Unit / mock-verified only. The tests in tests/nosql/typesense.test.ts are pure unit tests: a hand-built mock client whose fluent, resource-scoped API is mimicked with chained jest spies (collections() / collections(name) returning a shared collection mock whose documents() / documents(id) return a shared documents mock) is injected via the constructor's client option. No live Typesense server is contacted and the real typesense driver is never required. What this proves: name/library identity, and the connection lifecycle (health.retrieve() check on connect(), idempotent second connect(), clean disconnect(), isConnected() false before connecting). createCollection() routing to collections().create(schema); deleteCollection() routing to collections(name).delete(); indexDocument()/search() routing through collections(name).documents(); getDocument()/deleteDocument() routing through collections(name).documents(id). Error handling: ConnectionError from getClient() before connect(), a failed health.retrieve() wrapped in ConnectionError, ECONNREFUSED mapped to a 'Connection refused' message, and collection/document errors wrapped in DatabaseError. What this does not prove: live execution against a real Typesense server. Call routing and payload shapes are verified against the driver'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