@beignet/provider-search-meilisearch
v0.0.48
Published
Meilisearch-backed search provider for Beignet
Maintainers
Readme
@beignet/provider-search-meilisearch
Meilisearch-backed SearchPort provider for Beignet applications.
The provider installs ctx.ports.search and adapts Beignet's provider-neutral
search API to Meilisearch indexes, documents, filters, sorting, facets, and
offset pagination.
createMeilisearchSearchProvider(...) returns the stable
MeilisearchSearchProvider type. MeilisearchConfig describes its validated
config; the Zod schema remains internal.
Install
bun add @beignet/provider-search-meilisearch @beignet/coreRegister
// server/providers.ts
import { createMeilisearchSearchProvider } from "@beignet/provider-search-meilisearch";
export const providers = [
createMeilisearchSearchProvider({
indexPrefix: "my_app",
}),
];Set MEILISEARCH_HOST for the default env-backed provider. Set
MEILISEARCH_API_KEY when your Meilisearch instance requires one. Environment
variables:
MEILISEARCH_HOST(required unless you passhostorclient)MEILISEARCH_API_KEYMEILISEARCH_INDEX_PREFIXMEILISEARCH_TIMEOUT_MS
beignet doctor --strict checks that installed Meilisearch providers are
registered in server/providers.ts and that MEILISEARCH_HOST is present in
app env examples or config when the env-backed provider is used.
You can also pass an existing client or direct connection options:
createMeilisearchSearchProvider({
host: "https://search.example.com",
apiKey: process.env.MEILISEARCH_API_KEY,
});Use
import { defineSearchIndex } from "@beignet/core/search";
type IssueSearchDocument = {
id: string;
tenantId: string;
key: string;
title: string;
status: "open" | "resolved";
createdAt: string;
};
export const issueSearchIndex = defineSearchIndex<IssueSearchDocument>(
"issues",
{
searchableAttributes: ["key", "title"],
filterableAttributes: ["tenantId", "status"],
sortableAttributes: ["createdAt"],
},
);
await ctx.ports.search.indexDocuments(issueSearchIndex, {
id: issue.id,
tenantId: issue.tenantId,
key: issue.key,
title: issue.title,
status: issue.status,
createdAt: issue.createdAt,
});
const results = await ctx.ports.search.search(issueSearchIndex, {
query: "billing",
filters: { tenantId },
sort: ["createdAt:desc"],
limit: 20,
});The Meilisearch adapter validates query fields before sending a request:
filters and facets must use fields declared in filterableAttributes,
sort must use fields declared in sortableAttributes, and field names must be
simple identifiers or dotted paths. Map raw request input to app-owned
allow-listed field names before passing it to SearchPort.
API
createMeilisearchSearch(options)
Creates a SearchPort from a Meilisearch-compatible client. Use this for tests
or custom provider composition.
createMeilisearchClient(options)
Creates the small fetch-backed client used by the provider. It sends JSON
requests to Meilisearch and throws MeilisearchHttpError for non-2xx responses.
createMeilisearchSearchProvider(options)
Creates a Beignet lifecycle provider that contributes:
ctx.ports.search, the standard BeignetSearchPortctx.ports.meilisearch, an escape hatch with the raw client, index prefix, andcheckHealth()helper
createMeilisearchSearchProvider()
Ready-to-register provider using MEILISEARCH_* environment variables.
Devtools
When @beignet/devtools or another provider instrumentation sink is installed
before this provider, indexing, deleting, search, and settings operations appear
under the Search watcher. Events include the index, operation, document count,
duration, and success or failure status; document bodies are not recorded.
Failure behavior
The env-backed provider throws during startup when MEILISEARCH_HOST is
missing. Meilisearch non-2xx responses throw MeilisearchHttpError, including
the request path, response status, and parsed or raw response body when
available. Plain-text or HTML proxy responses are preserved on the error's
body instead of surfacing as an unrelated JSON parse failure. Indexing
operations in Meilisearch are asynchronous: a successful write means the task
was accepted, not that the document is immediately searchable.
Use ctx.ports.meilisearch.checkHealth() from app-owned readiness endpoints to
call Meilisearch's /health endpoint without indexing or searching documents.
Local and tests
Use a fake or in-memory SearchPort in use-case tests so tests can assert
search intent without depending on Meilisearch task timing. Use the direct
createMeilisearchSearch(...) factory with a test client for provider adapter
tests.
Deployment notes
Treat Meilisearch as a read model. Feed it from committed app state through outbox, listeners, jobs, or backfill tasks, and make stale-search behavior acceptable in the UI and API.
Correctness note
Search indexes are read models. Keep transactional truth in your database and use search for discovery, filtering, ranking, and faceted browsing. For durable indexing, pair this port with Beignet outbox/listener workflows or operational backfill tasks.
