# {{projectName}} / {{appName}}

Voltro API scaffold (template: **api-search**) — full-text search that stays
in sync with your tables, powered by `@voltro/plugin-search`.

## Boot

```bash
pnpm install
pnpm --filter @{{projectName}}/{{appName}} dev
# → http://localhost:4000  (store: memory — zero infra)
```

The boot log shows `search: backfilled 4 article(s) into the index` — the
demo seed's rows, indexed and ready to search.

## What this shows

- **Index stays in sync, automatically** — `searchPlugin` taps the post-commit
  ChangeEvent stream: every `articles` insert/update upserts its doc into the
  index, every delete removes it. No app code — create an article and it's
  searchable on the next request.
- **Tenant-scoped results** — the index spec's `tenantField: 'tenantId'` makes
  the synthesized `search.query` rpc auto-filter to the caller's tenant. One
  tenant can never see another's hits.
- **Backfill existing rows** — `startup/searchBackfill.startup.tsx` calls
  `backfillIndex(...)` on boot to seed the index from rows already in the table
  (the seed data, or every row after a restart / engine switch).
- **One config line to go to production** — `lib/search.ts` uses
  `memoryBackend()` (in-process, zero infra). Swap it for `{ engine:
  'typesense' | 'meilisearch' | 'algolia', url, apiKey }` and nothing else
  changes — the indexing + query code is identical.

## Try it

The plugin synthesizes a `search.query` rpc. Hit it over the inspect surface:

```bash
# Search the seeded articles (tenant 'acme' via the x-tenant default):
curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
  -d '{"tag":"search.query","input":{"index":"articles","q":"tenant"}}'

# Create an article — it's indexed on commit, then searchable:
curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
  -d '{"tag":"articles.create","input":{"tenantId":"acme","title":"Reactive queries","body":"Subscriptions push deltas live.","tag":"guide"}}'
curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
  -d '{"tag":"search.query","input":{"index":"articles","q":"reactive"}}'
```

## On the web side

A browser hook ships at `@voltro/plugin-search/web`:

```tsx
import { useSearch } from '@voltro/plugin-search/web'
const { results, search, pending } = useSearch('articles')
// search('reactive') → results auto-scoped to the signed-in tenant
```

## Files

- `lib/search.ts` — the shared backend instance + the `articles` index spec.
- `app.config.ts` — wires `searchPlugin({ backend, indexes })`.
- `database/schema.ts` — the `articles` table (tenant-scoped, reactive).
- `mutations/articles.create.*` — writes that auto-index via the tap.
- `queries/articles.list.*` — a live list to sit next to the search box.
- `seeds/articles.seed.ts` — demo data.
- `startup/searchBackfill.startup.tsx` — backfills the index on boot.
