Skip to content

FRD: Search Provider Wrapper

Document Summary

FieldDetails
Feature NameSearch Provider Wrapper
StatusDraft
OwnerDavid Holmes
ContributorsEngineering
Target Releasev2.0.0
Related LinksADR-051 (Provider Pattern), ADR-027 (Default Tech Stack), ADR-014 (Open Source First), src/components/widgets/global-search-bar.tsx, src/components/ui/search-input.tsx
Last Updated2026-06-02
Open Source Librariesmeilisearch
DocumentationMeilisearch Docs · JS SDK Reference · API Reference · Instant Search (React)

This FRD ships @dmwd-io/search as an optional package that re-exports meilisearch as the platform standard for full-text search via Meilisearch. The value is standards enforcement and drift prevention, not a custom abstraction. Per ADR-014, the library’s own API is the interface.


Introduction

Overview

The design system provides global-search-bar and search-input UI components, but there is no shared standard for executing search queries, handling facets, or providing autocomplete suggestions. Each consuming app integrates its own search backend directly, duplicating configuration and diverging on environment variable names. Per ADR-014 (Open Source First), we designate meilisearch as the community library for full-text search, re-export it through @dmwd-io/search, and document MEILISEARCH_HOST and MEILISEARCH_API_KEY as the platform env var conventions. No custom provider interface is built — the library’s own API is the interface.

Goals

  • Designate meilisearch as the platform standard for full-text search per ADR-014.
  • Ship @dmwd-io/search as a thin re-export of meilisearch with platform conventions layered on top.
  • Document MEILISEARCH_HOST and MEILISEARCH_API_KEY as the canonical env var names across all apps.
  • Define a typed SearchResult schema that the existing global-search-bar widget can consume directly.
  • Support autocomplete/suggestions as a first-class operation for type-ahead UI patterns.
  • Include a search page recipe example showing query, facets, and pagination wired together.

Non-Goals

  • Building a custom provider interface or adapter layer — the library’s own API is the interface (per ADR-014).
  • Algolia or Elasticsearch adapters (not designated by ADR-027).
  • Implementing server-side indexing or crawling logic.
  • Building new search UI components beyond integrating with existing widgets.
  • Relevance tuning or ranking algorithm design.
  • Geospatial or vector search support.

Scope

In Scope

AreaDescription
@dmwd-io/search packageThin re-export of meilisearch with platform env var conventions
Env var conventionsMEILISEARCH_HOST and MEILISEARCH_API_KEY documented as canonical names
Type re-exportsRe-export meilisearch types used by global-search-bar and search-input
PaginationUse meilisearch native pagination support
Faceted filteringUse meilisearch native facet support
AutocompleteUse meilisearch native suggestion/multi-search support
Recipe exampleA search page example composing query input, facets sidebar, and result list

Out of Scope

AreaReason
Custom SearchProvider interface or adapter layerADR-014: library API is the interface; no custom abstraction
Algolia or Elasticsearch adaptersNot designated by ADR-027
Index management (create, update, delete indexes)Admin operation handled outside the search contract
Document ingestion / crawlingBackend concern, not a package responsibility
Vector / semantic searchFuture extension; current scope is keyword full-text search
Search analytics (click tracking, conversion)Separate concern; may overlap with analytics provider

Users and Pain Points

User Groups

UserDescriptionNeeds
App developersEngineers building search featuresA single typed interface for search that works with any backend
QA engineersTesters validating search behaviorDeterministic mock results for specific queries
Design system maintainersLibrary contributorsA clean contract that the existing search widgets can consume

Pain Points

UserPain PointImpact
App developersEach app writes its own Meilisearch integration with different env var names and result shapesglobal-search-bar cannot consume results without app-specific adapters
App developersNo way to test search behavior in Storybook without a running search backendSearch widgets are demoed with hard-coded arrays, not realistic query/response cycles
App developersFaceted filtering logic is reimplemented in every appInconsistent facet behavior; some apps support multi-select, others do not

Definitions

TermDefinition
Search hitA single document/record matching a query, with highlighted snippets
FacetA filterable dimension of the search results (e.g. category, tag, date range)
Facet valueOne option within a facet, with a count of matching documents
SuggestionAn autocomplete candidate for type-ahead UI
HighlightA snippet of matched text with emphasis markers for rendering bold/highlighted matches
IndexA named collection of searchable documents (the provider queries an index by name)

Current State

Existing Behavior

global-search-bar.tsx renders a search input with a dropdown results panel. It accepts onSearch and results props but manages no data fetching internally. search-input.tsx is a lower-level input component with debounce and clear button support. Neither component has a standard data contract for query parameters or result shapes.

Current Limitations

  • No shared TypeScript types for search queries, results, or facets.
  • No mock search backend — Storybook stories use static arrays.
  • No autocomplete support at the data layer — the global-search-bar calls a prop callback, but there is no standard suggestion type.
  • No pagination contract — apps implement their own cursor or offset logic.

Existing Workarounds

  • Apps pass custom result objects to global-search-bar and map them in the component’s render callback.
  • Storybook stories hard-code 3-4 result objects that do not exercise pagination or facets.

Proposed Solution

Summary

Per ADR-014 (Open Source First), @dmwd-io/search re-exports meilisearch directly. No custom interface is built — the library’s API is the interface. The package adds platform conventions: canonical env var names (MEILISEARCH_HOST, MEILISEARCH_API_KEY) and a pre-configured client factory that reads those env vars. Consumers import from @dmwd-io/search instead of meilisearch directly, gaining drift prevention and standardized configuration without losing any library capability.

import { MeiliSearch } from '@dmwd-io/search';
const client = new MeiliSearch({
host: process.env.MEILISEARCH_HOST!,
apiKey: process.env.MEILISEARCH_API_KEY,
});

Key Capabilities

  • Full-text search, faceted filtering, and pagination using meilisearch’s native API.
  • Autocomplete suggestions via meilisearch multi-search or index-level suggestions.
  • Highlight support for rendering matched text using the library’s built-in formatter.
  • Pre-configured client factory (createSearchClient()) that reads platform env vars.

User Experience

End users benefit from consistent search behavior across apps — same facet interaction patterns, same pagination UX, same autocomplete speed.

Developer Experience

Developers import from @dmwd-io/search and use the meilisearch API directly. No adapter or factory abstraction to learn. The package guarantees all apps use the same library version and env var names. Storybook stories and tests use meilisearch’s own test utilities or a local Meilisearch instance.


Requirements

IDRequirementPriorityNotes
FR-001@dmwd-io/search re-exports all public exports from meilisearchMust-
FR-002Export a createSearchClient() factory that reads MEILISEARCH_HOST and MEILISEARCH_API_KEYMust-
FR-003Document MEILISEARCH_HOST and MEILISEARCH_API_KEY as the canonical platform env var namesMust-
FR-004Re-export meilisearch types used by global-search-bar and search-inputMust-
FR-005Include a recipe example wiring global-search-bar to a Meilisearch indexShould-

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001createSearchClient() reads MEILISEARCH_HOST and MEILISEARCH_API_KEY from the environmentApps configure once; all consumers share the same client patternMust
FUNC-002All meilisearch search, filter, facet, and pagination APIs are available via @dmwd-io/searchNo capability loss compared to importing from meilisearch directlyMust
FUNC-003Autocomplete/suggestions work via meilisearch multi-search or index-level suggestType-ahead autocomplete in the search barShould
FUNC-004Recipe example shows facet sidebar, query, and pagination wired to a live Meilisearch indexApp developers can copy-paste a complete integrationShould

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001@dmwd-io/search adds no runtime logic beyond re-exporting meilisearchPerformanceMust
NFR-002meilisearch is declared as a peer dependency, not bundledBundle sizeMust
NFR-003createSearchClient() validates that MEILISEARCH_HOST is set and throws a clear error if missingReliabilityMust
NFR-004Package exports TypeScript types without requiring extra @types/* packagesDeveloper experienceShould

API / Interface Requirements

Public API

NameTypeDescriptionRequired
MeiliSearchclass (re-export)Main client class from meilisearchYes
createSearchClientfunctionFactory reading MEILISEARCH_HOST / MEILISEARCH_API_KEYYes
All meilisearch typestypes (re-export)SearchResponse, Hits, FacetDistribution, etc.Yes

Example Usage

import { MeiliSearch, createSearchClient } from '@dmwd-io/search';
// Option A: platform factory (reads env vars)
const client = createSearchClient();
// Option B: explicit config
const client2 = new MeiliSearch({
host: process.env.MEILISEARCH_HOST!,
apiKey: process.env.MEILISEARCH_API_KEY,
});
const results = await client.index('docs').search('button', {
facets: ['category'],
hitsPerPage: 10,
});

API Notes

  • All meilisearch APIs are available; no functionality is removed or wrapped.
  • createSearchClient() is a convenience for the common case — direct instantiation with new MeiliSearch({...}) is equally valid.
  • The platform env var names (MEILISEARCH_HOST, MEILISEARCH_API_KEY) are the only conventions enforced by this package.

Accessibility Requirements

IDRequirementNotes
A11Y-001Package is a data layer — accessibility requirements apply to consuming componentsglobal-search-bar and search-input handle their own a11y
A11Y-002Search suggestion text must be plain text (not HTML) so consuming components can set aria-label correctlyNo HTML in suggestion strings
A11Y-003Total hit counts must be available in search responses so consuming components can announce “N results found” to screen readersEnables aria-live announcements

Checklist

  • Keyboard support is defined. (N/A — no UI)
  • Focus behavior is defined. (N/A — no UI)
  • Screen reader behavior is defined. (N/A — no UI)
  • Color contrast requirements are met. (N/A — no UI)
  • Reduced motion behavior is considered. (N/A — no UI)
  • Semantic HTML expectations are documented. (N/A — no UI)
  • ARIA usage is defined only where needed. (N/A — no UI)

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Storybook docs page explaining the platform search standard and @dmwd-io/search packageStorybookMust
DOC-002Inline JSDoc on createSearchClient() and any platform-specific exportsSource codeMust
DOC-003Recipe example: search page with query input, facets sidebar, and paginated resultsStorybookMust
DOC-004Example: wiring global-search-bar with createSearchClient() for autocompleteStorybookShould

Dependencies

DependencyTypeOwnerStatusNotes
meilisearch npm packageLibraryOpen sourceReadyRe-exported as @dmwd-io/search
ADR-014 Open Source FirstArchitectureEngineeringReadyDrives thin re-export approach
ADR-027 default tech stackArchitectureEngineeringReadyDesignates Meilisearch (small/medium) and Typesense (large) as default search implementations
global-search-bar.tsxComponentDesign systemReadyExisting widget to integrate with
search-input.tsxComponentDesign systemReadyLower-level input component

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
Library API may not cover advanced platform conventions (PII sanitization, audit logging)Some apps may still need thin wrappersDocument extension points in the recipe; keep wrappers local to the app
Thin re-export means meilisearch breaking changes surface directlyUpgrading the library is a platform-wide concernPin meilisearch version in @dmwd-io/search; publish changelogs on upgrades
Facet type system in meilisearch may not handle all filter shapes (range, date, nested)Complex facets require app-level workaroundsDocument known limitations in the recipe; use meilisearch filter syntax directly

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should createSearchClient() support a sort parameter default, or leave sorting entirely to the caller?David HolmesOpen
Q-002Should the package re-export Typesense as an optional large-scale alternative per ADR-027?David HolmesOpen
Q-003Should highlights return structured spans or use meilisearch’s default marker format?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001import { MeiliSearch } from '@dmwd-io/search' resolves to the meilisearch library classFR-001
AC-002createSearchClient() reads MEILISEARCH_HOST and MEILISEARCH_API_KEY and returns a configured clientFR-002
AC-003createSearchClient() throws a clear error when MEILISEARCH_HOST is not setNFR-003
AC-004All meilisearch public types are re-exported from @dmwd-io/searchFR-004
AC-005Storybook recipe example demonstrates a search page with query, facets, and paginationDOC-003
AC-006pnpm typecheck passes with no errorsNFR-004
AC-007No custom provider interface or adapter layer is implementedADR-014

LLM Handoff Instructions

Expected LLM Behavior

  • Create packages/search/ if it does not exist.
  • Add a package.json declaring meilisearch as a peer dependency and @dmwd-io/search as the package name.
  • Create packages/search/src/index.ts with export * from 'meilisearch' plus the createSearchClient() factory.
  • createSearchClient() must read process.env.MEILISEARCH_HOST and process.env.MEILISEARCH_API_KEY and throw a descriptive error if MEILISEARCH_HOST is not set.
  • Document MEILISEARCH_HOST and MEILISEARCH_API_KEY as the canonical platform env var names in JSDoc and the Storybook docs page.
  • Run pnpm typecheck before declaring the task complete.

LLM Should Not

  • Build a custom SearchProvider interface, adapter, or factory abstraction — the library’s API is the interface per ADR-014.
  • Import Algolia or Elasticsearch SDKs.
  • Modify existing UI components.
  • Bundle meilisearch — declare it as a peer dependency.
  • Implement indexing, crawling, or document ingestion.

Decision Log

DateDecisionReasonOwner
2026-05-26Use plain object filters (not query DSL) in search queriesKeeps integration simple and vendor-neutral; complex queries use Meilisearch filter syntax directlyDavid Holmes
2026-05-26Start with keyword full-text search, not vector/semantic searchMost apps need keyword search first; vector search is a future extensionDavid Holmes
2026-05-26Meilisearch designated as the default search adapterADR-027 §4 designates Meilisearch for small/medium deployments and Typesense for large; Algolia is not in the platform stackDavid Holmes
2026-06-02Reframed per ADR-014 (Open Source First): adopt meilisearch as thin re-export rather than building a custom provider interface. Library API is the interface.ADR-014 requires adopting community libraries before building custom abstractionsDavid Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft
2026-06-02David HolmesReframed per ADR-014: ship as thin re-export of meilisearch, drop custom interface.