Firestore Indexes

Indexes are internal lookup tables that Firestore maintains to make queries fast. Without the right index, Firestore cannot run certain queries at all. Understanding when Firestore creates indexes automatically and when you must create them manually prevents query failures in production.

Why Indexes Exist

A library with 100,000 books and no catalog makes every book search a full search through all shelves. A catalog lets you jump directly to the right shelf. Indexes are Firestore's catalog — they pre-sort data so queries return results instantly without scanning every document.

Single-Field Indexes — Automatic

Firestore automatically creates a single-field index for every field in every document. This means queries that filter or sort on a single field work without any setup:

// These work automatically — single-field index exists
where("status", "==", "published")
orderBy("publishedAt", "desc")
where("authorId", "==", "uid_alice")

Composite Indexes — Manual

Queries that filter or sort on multiple fields need a composite index — an index that covers that specific combination of fields in that specific order. Firestore does not create these automatically.

// This query needs a composite index:
query(
  collection(db, "posts"),
  where("status", "==", "published"),
  orderBy("publishedAt", "desc")
)

If you run this query without the index, Firestore throws an error in the browser console. The error message contains a direct link — click it to open the Firebase Console on the index creation page with all fields pre-filled. Click Create index and wait a few minutes for it to build.

Creating an Index Manually

Go to Firestore Database > Indexes > Composite in the Firebase Console. Click Add index. Fill in:

  • Collection ID: posts
  • Fields: status (Ascending), publishedAt (Descending)
  • Query scope: Collection (or Collection group for subcollections)

Click Create. The index shows "Building" status and becomes active in a few minutes to a few hours depending on data size.

Defining Indexes in Code with firestore.indexes.json

For production projects managed with the Firebase CLI, define indexes in a configuration file instead of through the console. This keeps your index definitions in version control:

// firestore.indexes.json
{
  "indexes": [
    {
      "collectionGroup": "posts",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "status", "order": "ASCENDING" },
        { "fieldPath": "publishedAt", "order": "DESCENDING" }
      ]
    },
    {
      "collectionGroup": "posts",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "authorId", "order": "ASCENDING" },
        { "fieldPath": "likesCount", "order": "DESCENDING" }
      ]
    }
  ],
  "fieldOverrides": []
}

Deploy indexes with:

firebase deploy --only firestore:indexes

Exempting Fields from Indexing

Firestore indexes every field by default. For fields that contain large text bodies (like article content) or fields you never query on, indexing wastes storage and increases write costs. Exempt those fields using field overrides:

{
  "fieldOverrides": [
    {
      "collectionGroup": "posts",
      "fieldPath": "body",
      "indexes": []  // disable all automatic indexes for this field
    }
  ]
}

Index Storage and Cost

Each index entry consumes storage. The free tier provides 1 GB of Firestore storage, which covers indexes and data combined. High-cardinality fields (fields with many unique values, like user IDs) produce large indexes. Monitor index size in the console under Usage.

Key Takeaway

Firestore creates single-field indexes automatically. Queries filtering or ordering on multiple fields need composite indexes that you create manually — either through the console link in error messages or through the firestore.indexes.json file deployed with the Firebase CLI. Exempt large text fields from indexing to reduce storage costs. Always test new query types in development to catch missing indexes before they appear in production.

Leave a Comment

Your email address will not be published. Required fields are marked *