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.