Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Perform Full-Text Search in Firebase

Firestore has no built-in full-text search. Prefix matching with >= and <= operators only works for starts-with queries. For real full-text search you need an external service like Algolia, Typesense, or Meilisearch. The typical pattern is: write data to Firestore, use a Cloud Function trigger to sync it to the search service, and query the search service from your client. Firebase Data Connect (PostgreSQL-based) now supports native full-text search as an alternative for new projects.

What you'll learn

  • Why Firestore cannot perform full-text search natively
  • How to implement prefix search with Firestore operators
  • How to integrate Algolia or Typesense for full-text search
  • How to sync Firestore data to a search index with Cloud Functions
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate8 min read15-20 minFirebase JS SDK v9+, Blaze plan (for Cloud Functions)March 2026RapidDev Engineering Team
TL;DR

Firestore has no built-in full-text search. Prefix matching with >= and <= operators only works for starts-with queries. For real full-text search you need an external service like Algolia, Typesense, or Meilisearch. The typical pattern is: write data to Firestore, use a Cloud Function trigger to sync it to the search service, and query the search service from your client. Firebase Data Connect (PostgreSQL-based) now supports native full-text search as an alternative for new projects.

Implementing Full-Text Search with Firebase

Firestore is built for fast reads on structured queries, not for searching inside text content. It has no LIKE operator, no trigram indexes, and no way to find a word in the middle of a string. This tutorial starts with what Firestore can do natively (prefix matching), then walks through integrating a dedicated search service like Algolia or Typesense using Cloud Functions to keep the search index in sync with your Firestore data.

Prerequisites

  • A Firebase project on the Blaze plan (required for Cloud Functions)
  • Firebase JS SDK v9+ installed in your project
  • An Algolia or Typesense account (free tiers available)
  • Basic understanding of Cloud Functions triggers

Step-by-step guide

1

Understand Firestore's search limitations

Firestore queries only support exact matches, range comparisons, and array membership. There is no contains, LIKE, or regex operator. The only text search you can do natively is prefix matching: find documents where a field starts with a given string. This is useful for autocomplete on names but inadequate for searching within descriptions, blog posts, or any longer text content.

typescript
1// What Firestore CAN do: prefix/starts-with search
2import { collection, query, where, orderBy, getDocs } from 'firebase/firestore'
3
4async function prefixSearch(field: string, prefix: string) {
5 // Matches documents where field starts with prefix
6 const q = query(
7 collection(db, 'products'),
8 where(field, '>=', prefix),
9 where(field, '<=', prefix + '\uf8ff'),
10 orderBy(field),
11 )
12 const snapshot = await getDocs(q)
13 return snapshot.docs.map((d) => ({ id: d.id, ...d.data() }))
14}
15
16// prefixSearch('name', 'Mac') → 'MacBook Pro', 'Mac Mini'
17// But searching for 'Pro' will NOT find 'MacBook Pro'

Expected result: Prefix search returns documents where the field starts with the given string. Mid-string and multi-word searches return no results.

2

Set up Algolia for full-text search

Algolia is the most commonly used search service with Firebase. Create an Algolia account, get your Application ID and Admin API Key, and install the algoliasearch package. Each Algolia index mirrors a Firestore collection — when you add or update a document in Firestore, a Cloud Function syncs it to Algolia. Client searches go directly to Algolia for instant results.

typescript
1// Install: npm install algoliasearch
2import algoliasearch from 'algoliasearch'
3
4// Initialize Algolia client (client-side uses Search-Only API Key)
5const algoliaClient = algoliasearch(
6 process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
7 process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY! // Search-only key, safe for client
8)
9
10const index = algoliaClient.initIndex('products')
11
12// Search products
13async function searchProducts(queryText: string) {
14 const { hits } = await index.search(queryText, {
15 hitsPerPage: 20,
16 attributesToRetrieve: ['name', 'description', 'price', 'objectID'],
17 })
18 return hits
19}
20
21// searchProducts('wireless headphones') → finds 'Sony Wireless Headphones',
22// 'Bose QuietComfort Headphones', etc.

Expected result: Algolia returns search results with typo tolerance, ranking, and highlighting in under 50ms.

3

Sync Firestore data to Algolia with a Cloud Function

Write Cloud Functions that trigger on Firestore document creates, updates, and deletes. Each function syncs the change to the Algolia index. Set the Algolia objectID to the Firestore document ID so updates and deletes target the correct record. Store the Algolia Admin API Key as a Cloud Secret, never in code.

typescript
1// functions/src/index.ts
2import { onDocumentCreated, onDocumentUpdated, onDocumentDeleted } from 'firebase-functions/v2/firestore'
3import { defineSecret } from 'firebase-functions/params'
4import algoliasearch from 'algoliasearch'
5
6const algoliaAppId = defineSecret('ALGOLIA_APP_ID')
7const algoliaAdminKey = defineSecret('ALGOLIA_ADMIN_KEY')
8
9function getIndex() {
10 const client = algoliasearch(algoliaAppId.value(), algoliaAdminKey.value())
11 return client.initIndex('products')
12}
13
14export const onProductCreated = onDocumentCreated(
15 { document: 'products/{productId}', secrets: [algoliaAppId, algoliaAdminKey] },
16 async (event) => {
17 const data = event.data?.data()
18 if (!data) return
19 const index = getIndex()
20 await index.saveObject({ objectID: event.params.productId, ...data })
21 }
22)
23
24export const onProductUpdated = onDocumentUpdated(
25 { document: 'products/{productId}', secrets: [algoliaAppId, algoliaAdminKey] },
26 async (event) => {
27 const data = event.data?.after.data()
28 if (!data) return
29 const index = getIndex()
30 await index.saveObject({ objectID: event.params.productId, ...data })
31 }
32)
33
34export const onProductDeleted = onDocumentDeleted(
35 { document: 'products/{productId}', secrets: [algoliaAppId, algoliaAdminKey] },
36 async (event) => {
37 const index = getIndex()
38 await index.deleteObject(event.params.productId)
39 }
40)

Expected result: Every Firestore write automatically syncs to Algolia, keeping the search index up to date.

4

Consider Typesense as an open-source alternative

Typesense is a free, open-source search engine that can be self-hosted or used as a managed service. It offers similar features to Algolia (typo tolerance, faceting, geosearch) with predictable pricing. Firebase has an official Typesense extension that handles the Firestore-to-Typesense sync automatically without writing Cloud Functions.

typescript
1// Install: npm install typesense
2import Typesense from 'typesense'
3
4const typesenseClient = new Typesense.Client({
5 nodes: [{
6 host: 'your-typesense-host.a1.typesense.net',
7 port: 443,
8 protocol: 'https',
9 }],
10 apiKey: process.env.NEXT_PUBLIC_TYPESENSE_SEARCH_KEY!,
11})
12
13async function searchProducts(queryText: string) {
14 const results = await typesenseClient
15 .collections('products')
16 .documents()
17 .search({
18 q: queryText,
19 query_by: 'name,description',
20 per_page: 20,
21 })
22 return results.hits?.map((hit: any) => hit.document) ?? []
23}
24
25// Firebase Extensions: install 'Search with Typesense'
26// from the Firebase Console to auto-sync Firestore → Typesense

Expected result: Full-text search works through Typesense with automatic Firestore sync via the Firebase extension.

5

Build a search UI component

Create a search input that queries the search service on every keystroke (debounced) and displays results. The search results include the Firestore document ID as objectID, which you can use to link back to the full document in your app or fetch additional data from Firestore if needed.

typescript
1import { useState, useEffect } from 'react'
2
3function SearchBar() {
4 const [query, setQuery] = useState('')
5 const [results, setResults] = useState<any[]>([])
6 const [loading, setLoading] = useState(false)
7
8 useEffect(() => {
9 if (!query.trim()) { setResults([]); return }
10
11 const timer = setTimeout(async () => {
12 setLoading(true)
13 const hits = await searchProducts(query)
14 setResults(hits)
15 setLoading(false)
16 }, 300) // 300ms debounce
17
18 return () => clearTimeout(timer)
19 }, [query])
20
21 return (
22 <div>
23 <input
24 type="text"
25 value={query}
26 onChange={(e) => setQuery(e.target.value)}
27 placeholder="Search products..."
28 />
29 {loading && <p>Searching...</p>}
30 {results.map((hit) => (
31 <div key={hit.objectID}>
32 <h3>{hit.name}</h3>
33 <p>{hit.description}</p>
34 </div>
35 ))}
36 </div>
37 )
38}

Expected result: A responsive search UI that shows results as the user types, powered by the external search service.

Complete working example

search-service.ts
1import algoliasearch from 'algoliasearch'
2
3const client = algoliasearch(
4 process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
5 process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
6)
7
8const productsIndex = client.initIndex('products')
9
10export interface SearchResult {
11 objectID: string
12 name: string
13 description: string
14 price: number
15 category: string
16}
17
18export async function searchProducts(
19 queryText: string,
20 options?: { category?: string; page?: number; hitsPerPage?: number }
21): Promise<{ hits: SearchResult[]; totalHits: number; totalPages: number }> {
22 const filters = options?.category
23 ? `category:${options.category}`
24 : ''
25
26 const { hits, nbHits, nbPages } = await productsIndex.search<SearchResult>(
27 queryText,
28 {
29 hitsPerPage: options?.hitsPerPage ?? 20,
30 page: options?.page ?? 0,
31 filters,
32 attributesToRetrieve: ['name', 'description', 'price', 'category'],
33 attributesToHighlight: ['name', 'description'],
34 }
35 )
36
37 return {
38 hits,
39 totalHits: nbHits,
40 totalPages: nbPages,
41 }
42}
43
44export async function prefixSearch(
45 db: any,
46 collectionName: string,
47 field: string,
48 prefix: string
49) {
50 const { collection, query, where, orderBy, limit, getDocs } = await import('firebase/firestore')
51 const q = query(
52 collection(db, collectionName),
53 where(field, '>=', prefix),
54 where(field, '<=', prefix + '\uf8ff'),
55 orderBy(field),
56 limit(10)
57 )
58 const snap = await getDocs(q)
59 return snap.docs.map((d) => ({ id: d.id, ...d.data() }))
60}

Common mistakes when performing Full-Text Search in Firebase

Why it's a problem: Assuming Firestore can search for words inside a text field like SQL LIKE '%keyword%'

How to avoid: Firestore has no contains or LIKE operator. Use a dedicated search service like Algolia or Typesense for full-text search.

Why it's a problem: Storing the Algolia Admin API Key in client-side code, exposing write access to the search index

How to avoid: Only use the Search-Only API Key on the client. The Admin Key belongs in Cloud Functions, stored as a Secret.

Why it's a problem: Not handling the initial backfill — only new documents get synced to the search index

How to avoid: Write a one-time migration script that reads all existing Firestore documents and batch-imports them into the search service before enabling the Cloud Function triggers.

Best practices

  • Use Firestore prefix search only for simple autocomplete on short fields like names or titles
  • Choose Algolia for managed search with enterprise features, or Typesense for open-source with predictable pricing
  • Store search API keys as Cloud Secrets using defineSecret, never hardcode them
  • Debounce search input by 200-300ms to reduce API calls and costs
  • Only index the fields you need to search — avoid sending entire documents to the search service
  • Use the Firebase Typesense extension for automatic sync without writing custom Cloud Functions
  • Consider Firebase Data Connect (PostgreSQL-based) for new projects that need native full-text search

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I need to add full-text search to my Firebase app. Firestore does not have native search. Show me how to integrate Algolia with Cloud Functions v2 that sync Firestore documents to an Algolia index on create, update, and delete. Include the client-side search query code.

Firebase Prompt

Add a search bar that searches products by name and description. Since Firestore does not support full-text search, integrate Algolia. Create Cloud Functions to sync the products collection to an Algolia index, and use algoliasearch on the client to show results with debounced input.

Frequently asked questions

Why does Firestore not support full-text search?

Firestore is designed for fast indexed lookups on structured data. Full-text search requires inverted indexes, tokenization, stemming, and ranking — features that would add complexity and cost to every Firestore instance. Google recommends using a dedicated search service.

Is Algolia free to use with Firebase?

Algolia has a free tier with 10,000 search requests per month and 10,000 records. For most small to medium apps, this is sufficient. Beyond that, pricing is based on search requests and records stored.

Can I use Firebase Data Connect for full-text search instead?

Yes. Firebase Data Connect uses Cloud SQL PostgreSQL, which supports native full-text search with tsvector and tsquery. This is a good option for new projects, but requires migrating away from Firestore for the searchable data.

How fast is the sync between Firestore and the search index?

Cloud Function triggers typically fire within 1-3 seconds of a Firestore write. Including the time to update the search index, new data is usually searchable within 2-5 seconds.

Can I search across multiple Firestore collections?

Yes. Create a single search index that includes documents from multiple collections, each with a type field to distinguish them. Your Cloud Functions can sync multiple collections to the same Algolia index.

Can RapidDev help integrate search into my Firebase app?

Yes. RapidDev can set up Algolia or Typesense integration with your Firebase project, including Cloud Function sync, search UI components, and faceted filtering tailored to your data model.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.