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
Area
Description
@dmwd-io/search package
Thin re-export of meilisearch with platform env var conventions
Env var conventions
MEILISEARCH_HOST and MEILISEARCH_API_KEY documented as canonical names
Type re-exports
Re-export meilisearch types used by global-search-bar and search-input
Pagination
Use meilisearch native pagination support
Faceted filtering
Use meilisearch native facet support
Autocomplete
Use meilisearch native suggestion/multi-search support
Recipe example
A search page example composing query input, facets sidebar, and result list
Out of Scope
Area
Reason
Custom SearchProvider interface or adapter layer
ADR-014: library API is the interface; no custom abstraction
Algolia or Elasticsearch adapters
Not designated by ADR-027
Index management (create, update, delete indexes)
Admin operation handled outside the search contract
Document ingestion / crawling
Backend concern, not a package responsibility
Vector / semantic search
Future 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
User
Description
Needs
App developers
Engineers building search features
A single typed interface for search that works with any backend
QA engineers
Testers validating search behavior
Deterministic mock results for specific queries
Design system maintainers
Library contributors
A clean contract that the existing search widgets can consume
Pain Points
User
Pain Point
Impact
App developers
Each app writes its own Meilisearch integration with different env var names and result shapes
global-search-bar cannot consume results without app-specific adapters
App developers
No way to test search behavior in Storybook without a running search backend
Search widgets are demoed with hard-coded arrays, not realistic query/response cycles
App developers
Faceted filtering logic is reimplemented in every app
Inconsistent facet behavior; some apps support multi-select, others do not
Definitions
Term
Definition
Search hit
A single document/record matching a query, with highlighted snippets
Facet
A filterable dimension of the search results (e.g. category, tag, date range)
Facet value
One option within a facet, with a count of matching documents
Suggestion
An autocomplete candidate for type-ahead UI
Highlight
A snippet of matched text with emphasis markers for rendering bold/highlighted matches
Index
A 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 = newMeiliSearch({
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
ID
Requirement
Priority
Notes
FR-001
@dmwd-io/search re-exports all public exports from meilisearch
Must
-
FR-002
Export a createSearchClient() factory that reads MEILISEARCH_HOST and MEILISEARCH_API_KEY
Must
-
FR-003
Document MEILISEARCH_HOST and MEILISEARCH_API_KEY as the canonical platform env var names
Must
-
FR-004
Re-export meilisearch types used by global-search-bar and search-input
Must
-
FR-005
Include a recipe example wiring global-search-bar to a Meilisearch index
Should
-
Priority Definitions
Priority
Meaning
Must
Required for this feature to ship.
Should
Important, but can be deferred if needed.
Could
Nice to have. Not required for initial release.
Functional Requirements
ID
Requirement
User Benefit
Priority
FUNC-001
createSearchClient() reads MEILISEARCH_HOST and MEILISEARCH_API_KEY from the environment
Apps configure once; all consumers share the same client pattern
Must
FUNC-002
All meilisearch search, filter, facet, and pagination APIs are available via @dmwd-io/search
No capability loss compared to importing from meilisearch directly
Must
FUNC-003
Autocomplete/suggestions work via meilisearch multi-search or index-level suggest
Type-ahead autocomplete in the search bar
Should
FUNC-004
Recipe example shows facet sidebar, query, and pagination wired to a live Meilisearch index
App developers can copy-paste a complete integration
Should
Non-Functional Requirements
ID
Requirement
Category
Priority
NFR-001
@dmwd-io/search adds no runtime logic beyond re-exporting meilisearch
Performance
Must
NFR-002
meilisearch is declared as a peer dependency, not bundled
Bundle size
Must
NFR-003
createSearchClient() validates that MEILISEARCH_HOST is set and throws a clear error if missing
Reliability
Must
NFR-004
Package exports TypeScript types without requiring extra @types/* packages
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
ID
Requirement
Notes
A11Y-001
Package is a data layer — accessibility requirements apply to consuming components
global-search-bar and search-input handle their own a11y
A11Y-002
Search suggestion text must be plain text (not HTML) so consuming components can set aria-label correctly
No HTML in suggestion strings
A11Y-003
Total hit counts must be available in search responses so consuming components can announce “N results found” to screen readers
Enables 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
ID
Requirement
Location
Priority
DOC-001
Storybook docs page explaining the platform search standard and @dmwd-io/search package
Storybook
Must
DOC-002
Inline JSDoc on createSearchClient() and any platform-specific exports
Source code
Must
DOC-003
Recipe example: search page with query input, facets sidebar, and paginated results
Storybook
Must
DOC-004
Example: wiring global-search-bar with createSearchClient() for autocomplete
Storybook
Should
Dependencies
Dependency
Type
Owner
Status
Notes
meilisearch npm package
Library
Open source
Ready
Re-exported as @dmwd-io/search
ADR-014 Open Source First
Architecture
Engineering
Ready
Drives thin re-export approach
ADR-027 default tech stack
Architecture
Engineering
Ready
Designates Meilisearch (small/medium) and Typesense (large) as default search implementations
global-search-bar.tsx
Component
Design system
Ready
Existing widget to integrate with
search-input.tsx
Component
Design system
Ready
Lower-level input component
Risks and Tradeoffs
Risk / Tradeoff
Impact
Mitigation
Library API may not cover advanced platform conventions (PII sanitization, audit logging)
Some apps may still need thin wrappers
Document extension points in the recipe; keep wrappers local to the app
Thin re-export means meilisearch breaking changes surface directly
Upgrading the library is a platform-wide concern
Pin 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 workarounds
Document known limitations in the recipe; use meilisearch filter syntax directly
Open Questions
ID
Question
Owner
Status
Resolution
Q-001
Should createSearchClient() support a sort parameter default, or leave sorting entirely to the caller?
David Holmes
Open
—
Q-002
Should the package re-export Typesense as an optional large-scale alternative per ADR-027?
David Holmes
Open
—
Q-003
Should highlights return structured spans or use meilisearch’s default marker format?
David Holmes
Open
—
Acceptance Criteria
ID
Criteria
Related Requirement
AC-001
import { MeiliSearch } from '@dmwd-io/search' resolves to the meilisearch library class
FR-001
AC-002
createSearchClient() reads MEILISEARCH_HOST and MEILISEARCH_API_KEY and returns a configured client
FR-002
AC-003
createSearchClient() throws a clear error when MEILISEARCH_HOST is not set
NFR-003
AC-004
All meilisearch public types are re-exported from @dmwd-io/search
FR-004
AC-005
Storybook recipe example demonstrates a search page with query, facets, and pagination
DOC-003
AC-006
pnpm typecheck passes with no errors
NFR-004
AC-007
No custom provider interface or adapter layer is implemented
ADR-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
Date
Decision
Reason
Owner
2026-05-26
Use plain object filters (not query DSL) in search queries
Keeps integration simple and vendor-neutral; complex queries use Meilisearch filter syntax directly
David Holmes
2026-05-26
Start with keyword full-text search, not vector/semantic search
Most apps need keyword search first; vector search is a future extension
David Holmes
2026-05-26
Meilisearch designated as the default search adapter
ADR-027 §4 designates Meilisearch for small/medium deployments and Typesense for large; Algolia is not in the platform stack
David Holmes
2026-06-02
Reframed 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 abstractions
David Holmes
Document History
Date
Author
Change
2026-05-26
David Holmes
Initial draft
2026-06-02
David Holmes
Reframed per ADR-014: ship as thin re-export of meilisearch, drop custom interface.