Standards Docs
Guides

Meilisearch

Configure @stndrds/adapter-meilisearch to index records on write and serve typed search queries.

@stndrds/adapter-meilisearch is the recommended search adapter for standards. It indexes records on write and serves typed queries from React hooks.

Breaking in vNEXT: DatabaseAdapter.search is now mandatory. Composing a DatabaseAdapter without a SearchAdapter fails to type-check and throws SearchAdapterRequiredError at bootstrap. The SearchAdapter lifecycle is also split: ensureIndex() creates the index, and applySchemaSettings(settings) applies the four lists (sortable, filterable, searchable, displayed) derived from the schema. The legacy ensureIndexes() (plural) has been removed.

Setup

Pass a MeilisearchSearchAdapter instance to SupabaseDatabaseAdapter as the search option. The adapter is mandatory — SupabaseDatabaseAdapter requires it at construction time.

// @noverify
import { MeilisearchSearchAdapter } from "@stndrds/adapter-meilisearch";
import { SupabaseDatabaseAdapter } from "@stndrds/adapter-supabase";
import { SchemaModule } from "@stndrds/adapter-nestjs";

const search = new MeilisearchSearchAdapter({
  host: process.env.MEILISEARCH_HOST!,
  apiKey: process.env.MEILISEARCH_API_KEY!,
});

const adapter = new SupabaseDatabaseAdapter(supabase, { search });

// In your NestJS app module:
SchemaModule.forRoot({
  adapter,
  registry,
  search: {
    reconcileOnStartup: true,      // compare PG vs Meili on boot
    reconcileIntervalMs: 900_000,  // periodic safety net (15 min)
    fullReindexThreshold: 0.2,     // reindex when delta > 20%
  },
})

The search key in SchemaModule.forRoot accepts a SearchConfig with three optional fields: reconcileOnStartup (default true), reconcileIntervalMs (default 15 minutes), and fullReindexThreshold (default 20%). All three have sensible defaults — the search key itself is optional.

Env vars

  • MEILISEARCH_HOST — Meilisearch URL (e.g. http://localhost:7700)
  • MEILISEARCH_API_KEY — admin key for indexing and settings updates

Use a master key in development. In production, scope your API key to the indexes standards creates (prefixed stndrds_ by default).

Sync triggers

The adapter hooks into the database adapter's CRUD events through the SchemaSearchModule registered by SchemaModule. Records sync to Meilisearch automatically on create, update, and delete — no manual reindex is needed for routine changes.

The sync path uses an event bus internally, so indexing is decoupled from the HTTP response. A write returns to the caller as soon as PostgreSQL commits; Meilisearch is updated asynchronously.

Manual reindex

@stndrds/adapter-meilisearch exports reindexAllRecords for bulk reindexing. Use it when setting up Meilisearch on an existing dataset, after a major schema change, or when reconcile reports a large delta.

// @noverify
import { reindexAllRecords } from "@stndrds/adapter-meilisearch";
import { withTenantContext, asTenantId } from "@stndrds/schema";

await withTenantContext(asTenantId("your-tenant-id"), async () => {
  const result = await reindexAllRecords(adapter, searchIndexer, {
    batchSize: 500,
    onProgress: (done, total) => console.log(`${done}/${total}`),
  });
  console.log(`Indexed ${result.indexed} records in ${result.duration}ms`);
});

reindexAllRecords requires a tenant context. In single-tenant setups, use the defaultTenantId configured in SchemaModule. The batchSize option (default 500) controls both the PostgreSQL query page size and the Meilisearch bulk-add chunk size.

A reconcile helper is also exported for differential sync — it compares PG and Meilisearch counts and only reindexes divergent records, which is faster than a full reindex on large datasets.

Records sync via the runtime SearchIndexer service. Schema-driven index settings — sortable, filterable, searchable, displayed — are computed by computeSchemaSettings(objects) and applied on schema.attribute.*, schema.object.*, and tenant.created events. You no longer need to manually dispatch settings refresh.

On this page