MeilisearchStore — Meilisearch search engine
Read this page in the documentation
MeilisearchStore — Meilisearch search engine Overview Meilisearch is a search engine built around an inverted index with typo-tolerant, prefix, full-text search and faceting rather than SQL: no JOIN, no transaction log, schema-flexible documents grouped into indexes (each identified by a uid and a primary key). Because none of that fits the SQL-shaped Dialect interface, MeilisearchStore implements the minimal NoSqlStore marker interface (src/nosql/store.ts) — connection lifecycle plus a getClient() escape hatch — and exposes Meilisearch's real capabilities grouped by concern: connection lifecycle, index management, and document CRUD/search. Most operations are scoped to an index and reached through client.index(uid).<op>(...); index lifecycle (createIndex/deleteIndex) and cluster stats live directly on the top-level client. Identity: Property | Value | --------- | --------------- | name | 'meilisearch' | library | 'meilisearch' | Lazy loading — not a hard dependency The official meilisearch JS driver is not a hard dependency of this package. It is loaded lazily via require('meilisearch') inside connect(), rather than a top-level import. Importing this module therefore does not force the dependency to be resolved unless a Meilisearch store is actually constructed and connected. Injected client MeilisearchStoreOptions 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 host/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() 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 | -------- | -------- | --------------------------------------------------------------------------------------------- | host | string | URL of the Meilisearch server, e.g. 'http://localhost:7700'. | apiKey | string | API key used to authenticate against the server. | client | any | An already-built client to use instead of building one. When set, host/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 meilisearch and builds a MeiliSearch client from host/apiKey, then runs client.health(). 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 meilisearch client for anything not wrapped here. Throws ConnectionError if not connected. | Index management Method | Signature | Behavior | ------------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------- | createIndex | createIndex(uid: string, options?: Record<string, unknown>): Promise<any> | Creates an index by uid via client.createIndex(uid, options), optionally specifying its primary key via options. Returns the enqueued task. | deleteIndex | deleteIndex(uid: string): Promise<any> | Deletes an index by uid via client.deleteIndex(uid). Returns the enqueued task. | Document CRUD + search Method | Signature | Behavior | ---------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | addDocuments | addDocuments(index: string, documents: Record<string, unknown>[], options?: Record<string, unknown>): Promise<any> | Adds (or replaces) documents in an index via client.index(index).addDocuments(documents, options). Returns the enqueued task. | search | search<T = Record<string, unknown>>(index: string, query: string, options?: Record<string, unknown>): Promise<T[]> | Runs a search via client.index(index).search(query, options) and returns the response's hits array (or [] when absent). | getDocument | getDocument<T = Record<string, unknown>>(index: string, id: string | number): Promise<T> | Gets a single document by its primary-key value via client.index(index).getDocument(id). | deleteDocument | deleteDocument(index: string, id: string | number): Promise<any> | Deletes a single document by its primary-key value via client.index(index).deleteDocument(id). Returns the enqueued task. | Stats Method | Signature | Behavior | ---------- | ------------------------ | ------------------------------------------------------------------------------------------- | getStats | getStats(): Promise<any> | Gets server-wide stats (database size, per-index document counts, etc.) via client.getStats(). | Example Verification status Unit / mock-verified only. The tests in tests/nosql/meilisearch.test.ts are pure unit tests: a hand-built mock client (with jest spies for every method the store touches, plus a chained index(uid) spy returning a shared per-index mock) is injected via the constructor's client option. No live Meilisearch server is contacted and the real meilisearch driver is never required. What this proves: name/library identity, and the connection lifecycle (health() check on connect(), idempotent second connect(), clean disconnect(), isConnected() false before connecting). createIndex()/deleteIndex() forwarding to the top-level client (with and without options); addDocuments()/search()/getDocument()/deleteDocument() scoping to index(uid) and forwarding their arguments; search() returning the hits array and [] when there are no hits; getStats() returning the body. Error handling: ConnectionError from getClient() before connect(), a failed health() wrapped in ConnectionError, ECONNREFUSED mapped to a 'Connection refused' message, and index/document errors wrapped in DatabaseError. What this does not prove: live execution against a real Meilisearch 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