A demographic query layer over a 2,026-row profiles table. Combinable filters, indexed sort, clamped pagination — and a rule-based natural-language endpoint that converts "young males from nigeria above 30" into the same filter object the typed query uses. No LLM, no embeddings, no external calls.
Natural language without an LLM
The /search endpoint runs a sequence of regex passes — gender, age-group keywords, the "young" bracket (16–24), comparators (above/below/older-than/under), age ranges (between N and M), and a longest-name-first country match against Intl.DisplayNames plus a curated alias table. "Male and female teenagers above 17" suppresses the gender filter on purpose; "banana" returns 400 with "Unable to interpret query." Every supported phrase has a worked example in the README.
Idempotent by design
The seeder enforces "no duplicate records" at four layers: input normalisation (lowercase names, uppercase ISO codes, fill missing country names from Intl.DisplayNames), an intra-file dedup pass through a Map, a DB pre-check that filters out existing names per batch, and a final upsert with onConflict ignoreDuplicates. The second run inserts zero rows and prints "Seed is up to date" — every time.
Query layer
Every filter column and every sort column carries a dedicated index. count: "exact" piggybacks the total onto the same SELECT so listing is one round-trip. Page and limit clamp instead of erroring on out-of-range values, because a paginated UI should never crash on a stale query string.