@samveys/meilisearch

Version
1.0.2Nov 5, 2026
Install
pnpm add @samveys/meilisearch

From packages/meilisearch/README.md in the framework repository.

A Meilisearch driver for @samveys/search. It talks to Meilisearch over its REST API with the global fetch, so it has no runtime dependencies. It passes the @samveys/search conformance suite against a real Meilisearch container.

Use it

ts
import { meilisearch } from '@samveys/meilisearch'
import type { SearchIndexDefinition } from '@samveys/search/contracts'

const driver = meilisearch({ host: 'http://127.0.0.1:7700', apiKey: process.env['MEILI_MASTER_KEY'] })

const reports: SearchIndexDefinition = {
  name: 'reports',
  fields: { title: { weight: 3 }, body: { weight: 1 } },
  filters: ['region'],
}

export async function demo(): Promise<readonly string[]> {
  await driver.ensureIndex(reports)
  await driver.upsert(reports, [{ id: '1', fields: { title: 'October revenue report', body: 'numbers' }, filters: { region: 'bali' } }])
  const { ids } = await driver.search(reports, { text: 'october revenue', filters: { region: 'bali' }, limit: 10, offset: 0 })
  return ids
}

Pass the driver wherever @samveys/search takes one (see that package for searchable()).

OptionMeaning
hostBase URL of Meilisearch
apiKeyMaster or admin key, sent as a bearer token. Never put in an error message
taskTimeoutMsHow long to wait for an indexing task. Default 60 000
requestTimeoutMsPer-request timeout. Default 30 000

Engine failures throw SamveysError SVS_E107.

How it behaves

  • Ranking. Meilisearch ranks by the order of searchableAttributes, not by numeric weights. The driver orders fields by descending weight, so a higher-weight field wins over a lower one. Equal weights keep declaration order.
  • Every word must match. Searches use matchingStrategy: "all". Quotes and a leading - in user text are removed, so they are plain words, not operators.
  • Consistency. Meilisearch indexes asynchronously. Every write waits for its task to succeed before it returns, so a document is searchable as soon as upsert, remove or flush resolves.
  • Totals. When offset is a multiple of limit, the total is exact (page mode). Otherwise Meilisearch only gives an estimate (estimatedTotalHits).
  • Filters. Values are quoted and escaped in the filter string. Filter names must be letters, digits and underscores.
  • Ids. Meilisearch only allows [A-Za-z0-9_-] in primary keys, so ids are stored base64url-encoded alongside the original.

Run the conformance test

sh
SVS_REQUIRE_DOCKER=1 node --no-warnings --test packages/meilisearch/test/conformance.test.ts

The test starts getmeili/meilisearch:v1.12 as samveys-conformance-meilisearch-<pid> and removes it afterwards. Without Docker it skips; SVS_DOCKER=0 forces a skip, and SVS_REQUIRE_DOCKER=1 makes a missing Docker a failure.