Query Firestore Data With React In 10 Steps: Where, Filter & Full-Text Search (2023)

Firebase is a powerful Backend-as-a-service platform that will give you superpowers as a React developer, but you need to learn a minimum to get started: in the following article, you’ll read how to query data stored in Firebase / Firestore from React in only 10 minutes.

Developing on Firebase can be daunting. There is a lot of documentation to go through, and the database interface isn’t particularly user-friendly. At Rowy, we provide a content management system on top of Google Firestore to help front-end developers use it without the hassles. To save ourselves an hour of configuration work, and many more hours down the road, we are going to use Rowy as a Firebase CMS for a React app.

1. Create A Rowy Account

First, follow the installation guide or use the Deploy shortcut to let Rowy guide you. It takes 5 minutes to get started:

Untitled

We name the project React Firestore Query. Rowy will take care of setting up Firestore, Firebase’s database management system.

You are ready to use Firestore with React as soon as your project is created:

Untitled

2. Create A Table And Add Data To It

We now create a database from scratch for our test app.

First, create a new table from a ready-to-use template. Click “Show all templates” on your Rowy project’s homepage and pick “Countries”:

Untitled

Rowy takes care of setting up a Firestore collection called countries and populate it with properties:

Untitled

The database is empty. We just need to import some data by using the Countries demo’s import button. Download the data from the demo as a CSV file:

Untitled

And then simply re-import it in your own countries table:

Untitled

That’s it! We now have a database that contains 244 data points, and it only took a minute!

Untitled

A NoSQL database like Firestore is a set of collections (called tables in Rowy) containing documents. In this example, our collection is a country list, and this collection contains 244 countries as single documents.

3. Obtain Your Firebase Config

Now that our test dataset is ready, we need to obtain the accesses to Firestore’s API to use in our front-end.

Head to your project’s Firebase Console and navigate to “Project settings”. In the first tab, you’ll find the following code that contains your Firebase configuration:

Untitled Before you head to VSCode, go to the side panel. Click BuildFirestore database and navigate to the Rules tab. During development, we need to disable authentication to avoid running into permission issues.

Add the following security rule:

match match /{document=**} {
    allow read, write: if true;
}

You will obtain something like this:

Untitled

User authentication is outside the scope of this article, but know that you’ll need some way to recognize your users to run an app in production. For now, keep your Firebase configuration a secret until the development phase is over.

4. Create A New React App

Create a new React app:

npx create-react-app react-firestore-query

We won’t need much code to get started, so go ahead and empty your App function:

src/App.js

function App() {
  return (
    <div className="App">
    </div>
  )
}

export default App

5. Get All Documents In A Firestore Collection

Use the Firebase config you obtained in step 3 to connect to your Firestore database:

src/services/db.mjs

import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore"; 

let db = false;

export const getDb = () => {
    if(!db){
        const firebaseConfig = {
            apiKey: <API_KEY>,
            authDomain: <AUTH_DOMAIN>,
            projectId: <PROJECT_ID>,
            storageBucket: <STORAGE_BUCKET>,
            messagingSenderId: <MESSAGING_SENDER_ID>,
            appId: <APP_ID>
        }

        const app = initializeApp(firebaseConfig)

        db = getFirestore(app)
    }

    return db
}

This code implements a singleton pattern that requests a Firestore database connection to perform queries. This way, we avoid reconnecting each time we send a request. It’s nice to avoid saturating the back-end server and decrease the Firestore bill.

Our countries collection already contains 244 documents, as you can see in Rowy, so we can go ahead and get them from Firestore to display them in React.

To keep the code modular, let’s create a standalone service to manage our API calls to Firebase:

src/services/countries.mjs

import { getDocs, collection } from "firebase/firestore"; 
import { getDb } from "./db.mjs"

const collection_name = "countries"

export const findAll = async () => {
    const doc_refs = await getDocs(collection(getDb(), collection_name))

    const res = []

    doc_refs.forEach(country => {
        res.push({
            id: country.id, 
            ...country.data()
        })
    })

    return res
}

The findAll function will fetch all the documents within the countries collection and return them as an array of countries, ready to be consumed by our React app.

Let’s create a CountryList component that will display all the countries:

src/App.js

import CountryList from './components/country-list.js'

function App() {
  return (
    <div className="App">
      <CountryList/>
    </div>
  )
}

export default App

The CountryList component encapsulates all the back-end logic, handles state changes, and displays the content:

src/components/country-list.js

import { useState, useEffect } from 'react'

import { findAll } from '../services/countries.mjs'

import CountryListItem from './country-list-item.js'

function CountryList() {
    const [loading, setLoading] = useState(false)
    const [countries, setCountries] = useState([])

    const fetchData = async () => {
        setLoading(true)

        const res = await findAll()

        setCountries([...res])
        setLoading(false)
    }

    useEffect(() => {
        fetchData()
    }, [])

    return (
        <section>
            <header>
                <h2>Countries</h2>
            </header>

            { loading && 
                <p>loading...</p>
            }

            <ul>
                {countries.length > 0 && countries.map(country => (
                    <CountryListItem country={country}/>
                ))}
            </ul>
        </section>
    )
}

export default CountryList

src/components/country-list-item.js

function CountryListItem(props) {
    const { country } = props

    return (
        <li key={country.id}>
            <h3>{country.flag} {country.name} ({country.code})</h3>
            <p>Capital: {country.capital}</p>
            <p>Continent: {country.continent}</p>
        </li>
    )
}

export default CountryListItem

That’s it! Just yarn start the React app and you’ll see your countries data stored in Rowy:

Untitled

6. Get A Single Document By ID

Similarly, you can also query Firestore to get a single document by id:

src/services/countries.mjs

import { doc, getDoc } from "firebase/firestore"; 
import { getDb } from "./db.mjs"

const collection_name = "countries"

export const findOne = async id => {
    const d = await getDoc(doc(getDb(), collection_name, id)) 
    return d.data()
}

7. Using The Firestore Query Syntax

We managed to request documents from Firestore, but what about more complex queries? That’s where the query function comes in. The following code is equivalent to retrieving the whole collection:

src/services/countries.mjs

const doc_refs = await getDocs(query(collection(getDb(), collection_name)))

Which gives us the exact same result:

Untitled

The only difference is the use of the function query, which is redundant here. The thing is query can take additional parameters to perform fine-grained requests, as we will now see.

8. Filtering Firestore Documents With Where

The where clause allows us to filter data according to one or more properties:

src/services/countries.mjs

export const findByContinent = async () => {
    const collection_ref = collection(getDb(), collection_name)
    const q = query(collection_ref, where("continent", "==", "Asia"))
    const doc_refs = await getDocs(q);

    const res = []

    doc_refs.forEach(country => {
        res.push({
            id: country.id, 
            ...country.data()
        })
    })

    return res
}

In this example, we obtain the list of countries based in Asia:

Untitled

9. Sorting Documents With Order By

We can also use the query function to sort our results with the orderby clause and limit their number with the limit clause. For example, let’s sort countries by alphabetical order and get the first 5 results:

src/services/countries.mjs

export const findByAlphabeticalOrder = async () => {
    const collection_ref = collection(getDb(), collection_name)
    const q = query(collection_ref, orderBy("name"), limit(5))
    const doc_refs = await getDocs(q);

    const res = []

    doc_refs.forEach(country => {
        res.push({
            id: country.id, 
            ...country.data()
        })
    })

    return res
}

Which returns the follow result:

Untitled

10. Full-Text Search

Firestore doesn't provide full-text search out-of-the-box, but you can easily integrate a search service like Meilisearch to do just that.

Check out our article on how to integrate Meilisearch with Rowy to learn more. It will walk you through the whole process in as little as 5 minutes:

  1. Connecting Meilisearch to Rowy
  2. Enabling full-text search on your Firebase collections
  3. Calling Meilisearch from your React app via API

It all works right away and you won't need to manage a single search index or write a single line of code thanks to Rowy's support.

Subscribe To Rowy

And this is how you perform basic queries with Firestore in React, folks!

If you haven’t subscribed to Rowy already, we’ve got plenty of templates to get you up and running in 2 minutes! So don’t hesitate and try Rowy for free. It will save you hours of back-end development time as your React code base evolves.

Get started with Rowy in minutes

Continue reading

Browse all