In-App Search
Search is one of those features that looks trivial until you build it. A box, a list, how hard can it be? Then a user types "expreso" instead of "espresso" and gets nothing, or your LIKE '%query%' query crawls once the table grows, or someone searches "running shoes" and the results come back in database insertion order instead of relevance order. Appsanic wires up Algolia as a managed connector inside the project that needs it; connections are not shared automatically with other projects.
Do you actually need a search service?
Most apps don't, at first. Be honest about which case you're in.
A database query is enough when:
- You're filtering a small list (a few hundred rows) by an exact field. "Show tasks where status is done."
- You're matching the start of a string. "Names that begin with what I typed."
- Results don't need to be ranked by relevance. Newest-first or alphabetical is fine.
- Typos don't matter because users pick from a known set.
Supabase (Postgres) handles all of that well, and Postgres even has decent built-in full-text search for medium-sized datasets. See Database & Data. Start there. Don't reach for a search service you don't need.
You want a real search service when:
- The dataset is large (tens of thousands of records and up) and
LIKEqueries have started to drag. - Users type freely and expect forgiveness for typos ("expreso" should still find espresso).
- You need results ranked by genuine relevance, with the best match first.
- You want results as the user types, updating on every keystroke in a few milliseconds.
- You need facets - those "filter by brand, price, colour" sidebars that update counts live.
That combination is exactly what Algolia is built for, and it's hard to recreate with a database alone.
The setup prompt
"Add instant search to the recipes screen. Search by title, ingredients, and tags. Be forgiving of typos, show results as I type, and let me filter by cuisine and difficulty. Use Algolia."
What the agent does:
- Reads your Algolia Application ID and search-only key from the connector and embeds them in the app (both are safe to bundle - more on that below).
- Builds the instant-search screen: a search box, a results list that updates on each keystroke, and the facet filters you asked for.
- Defines which fields are searchable (title, ingredients, tags) and which are facets (cuisine, difficulty).
- Explains that indexing is separate and generates a server-side sync plan when requested; you deploy it and provision its write key.
Connect Algolia inside the project and describe what you want searched. Other projects keep separate connections.
Two keys, and why it matters
Algolia gives you two credentials, and the difference is the whole security story:
- The Application ID identifies your Algolia account. Not secret.
- The search-only key can run queries and cannot write. If embedded, every record and index it can reach must be safe for every app user to read. Restrict it to the minimum indexes, operations, record count, and rate limits. See Connectors Overview.
- The admin key can write and delete everything in your index. It is a secret. It must never be bundled into an app people download. The agent never embeds it, and you should never paste it into a prompt.
For private/per-user records, do not use one global embedded key as authorization. A backend should issue short-lived secured Algolia keys with mandatory user filters and index restrictions. Indexing still happens server-side, with a separate write key held somewhere the public never sees.
Indexing your data
Algolia doesn't search your database directly. It keeps its own optimised copy of the records you want searchable, called an index. You push records to it, and Algolia builds the structures that make search instant.
A record is just a JSON object. For a recipes app, one record might be:
{
"objectID": "recipe_482",
"title": "Espresso Martini",
"ingredients": ["vodka", "coffee liqueur", "espresso"],
"cuisine": "cocktail",
"difficulty": "easy",
"rating": 4.7
}The objectID is the only required field - it's how Algolia recognises a record so that re-pushing it updates rather than duplicates. Use your database row's primary key and you get safe, repeatable updates for free.
The first push is a one-off backfill: send every existing row up to the index. After that, the index only needs to learn about changes.
Keeping the index in sync
This is the part people forget, and then wonder why a newly added recipe never appears in search. The index is a copy, so when a record changes in your database, the index has to be told. There are two clean patterns the agent can build, both server-side so the admin key stays put:
A Supabase database trigger plus an Edge Function (the tidy default). Add a trigger so an insert, update, or delete calls a narrow function. The agent can generate the function and setup steps, but you deploy it and add a dedicated Algolia admin key directly to its secret manager. Neither the search-only client key nor the saved connector secret should be used as the indexing credential.
"When a recipe is added, edited, or deleted in Supabase, sync that single record to Algolia automatically. Keep the Algolia admin key server-side in a Supabase Edge Function, never in the app."
A scheduled re-sync (simpler, slightly behind). A scheduled function runs every few minutes (or nightly) and pushes anything that changed since it last ran. Easier to reason about, but search lags reality by up to one interval. Good for catalogues that don't change by the second.
Whichever you choose, two habits save pain later:
- Send only the fields you search, facet, or display. A leaner record is a faster, cheaper index. Don't mirror your whole table.
- Never put anything sensitive in the index. The search-only key is in the app, so treat every indexed field as effectively public. No private notes, no internal flags, no other users' data.
Instant-search UI patterns
"Instant" search means results update as the user types, not after they press enter. A few patterns make it feel right:
- Debounce the keystrokes. Don't fire a query on every single letter the instant it lands - wait a beat (around 50 to 150 ms) after the user stops typing. Fewer queries, smoother feel, smaller bill.
- Show the empty state. Before anyone types, suggest recent searches or popular results rather than a blank screen.
- Handle no results gracefully. "Nothing for 'expreso martini'" with a suggestion beats a silent blank list. Typo tolerance means this happens less than you'd fear.
- Highlight the match. Algolia returns which part of each result matched the query, so you can bold the matching letters. It makes results feel responsive and accurate.
- Keep the list short and tappable. On a phone, the top handful of results is what matters. Paginate or lazy-load the rest as the user scrolls.
"Show search results as I type with a short debounce, highlight the matching text in each result, and show popular recipes before I've typed anything."
Ranking, typo tolerance, and facets
These three are why a search service earns its keep.
Ranking decides the order results come back in. Algolia ranks by how well a record matches first, then breaks ties using a field you choose - rating, popularity, recency, distance. Tell the agent what "best" means for your app:
"Rank search results by relevance first, then by rating, then by number of reviews."
Typo tolerance is on by default and is the single biggest reason to use Algolia over a raw database query. "expreso", "esspresso", and "expresso" all find espresso. You can tune how forgiving it is (stricter for short words, off for things like product codes where a typo means a genuinely different item).
Facets are the filterable attributes that power "filter by" UIs - cuisine, brand, price band, colour. Mark a field as a facet and Algolia can both filter on it and return live counts ("Italian (42), Thai (17)") that update as other filters change. That live count is hard to do well against a database and trivial with facets.
"Make cuisine and difficulty filterable facets. Show the count next to each option and update the counts as filters change."
Costs to keep an eye on
Algolia's current plans can meter records and search requests differently. Two practical consequences remain:
- A debounce isn't just for feel - every keystroke without one is a billable search request.
- Indexing only the fields you need keeps record count and size down.
Check Algolia's current pricing page for the live numbers; their tiers change over time.
Under the hood (for engineers)
- The generated preview uses plain
fetchagainst Algolia's REST API because Algolia client packages are not in the preview dependency allowlist. It inlines the Application ID and global search-only key only for client-readable data. - The admin (indexing) key never touches the client bundle. Writes happen server-side: a Supabase Edge Function (or another small relay) holds the admin key in its own environment and is the only thing that pushes, updates, or deletes records. This mirrors the secret-key relay pattern in Connectors Overview - the only difference is that Algolia's read key is publishable, so reads run directly from the app.
- A generated sync is a deployment plan, not a default running service. You choose event-driven or scheduled sync, deploy it, apply any database trigger, and provision the indexing key directly in its secret manager.
- Search runs against Algolia's index, not your Postgres tables, so search load never touches your database. The two are kept consistent by the sync layer, not by querying live - which is why a missed sync, not a slow query, is the usual cause of "this record won't show up".
- With the global embedded key, treat every reachable indexed field as public. Private search needs backend-issued secured keys in addition to source-database RLS. See Database & Data.
Next
Read In-app ads for adding AdMob ad units and earning from a free app.
