Speeding Up FaunaDB Lookups: How to Build and Use Indexes for Low‑Latency Queries
FaunaDB indexes let you turn slow, full‑scan lookups into lightning‑fast queries. This guide walks through creating a users‑by‑email index, shows a concrete FQL example, and discusses trade‑offs like storage overhead and write latency.
16 Aug 2026, 11:53 UTC

Problem: Slow Lookups on Non‑Primary Fields
In a growing application, you often need to find a user by their email, a product by its SKU, or a comment by its author ID. If you query the collection directly with Match on a field that isn’t the collection’s primary key, FaunaDB performs a full scan of the collection. For small tables this is fine, but as the dataset grows the latency rises linearly and can become unacceptable for real‑time user interfaces.
Index Basics: Immutable, Serverless Paths
FaunaDB indexes are immutable definitions that the engine maintains automatically. Once you create an index, every write to the source collection updates the index in the background, so you never have to rebuild it manually. Indexes can be defined on any field in a collection’s data object, and you can specify one or more terms that determine the searchable keys.
Key points:
termsdefine the fields you’ll match against.- Indexes are sparse by default; they only include documents where all terms are present.
- Creating an index does not change your data model; it only adds a new query path.
Building a Practical Index: Users by Email
Let’s walk through creating an index on the email field of a users collection and then querying it. The following FQL runs in the Fauna dashboard or via the CLI and requires admin permissions.
Let(
{ index: CreateIndex({
name: "users_by_email",
source: Collection("users"),
terms: [{ field: ["data", "email"] }]
})
},
{
result: Match(index, "[contact removed]")
}
)
Breakdown:
CreateIndexdefines the index name, source collection, and term field.- The
termsarray tells Fauna to index theemailfield inside thedataobject. - After the index is populated (which happens automatically), you can use
Matchto retrieve the document by email inO(log n)time.
To verify the index is active, open the Fauna console, navigate to the index, and check the populated flag. A populated index shows a steady count that matches the number of documents with an email field.
Querying with the Index: Performance Check
Run the same lookup without the index:
Let(
{ doc: Get(Match(Ref(Collection("users")), "[contact removed]")) },
{ result: doc }
)
Because Match on the collection’s primary key is a full scan when the key isn’t indexed, you’ll notice higher latency. Compare the execution times in the console’s Query History. A properly populated index should reduce latency by an order of magnitude for large collections.
Trade‑offs & Limitations
Indexes consume additional storage and add a small amount of write latency because each write must update every relevant index. For a collection with millions of documents, the storage overhead can be significant if you create many indexes.
To mitigate:
- Use sparse indexes (the default) so that documents missing the indexed field are excluded.
- Only index fields that are queried frequently or are part of a composite key.
- Drop unused indexes with
DeleteIndexto reclaim storage.
Remember that index definitions are immutable. If you need to change the indexed fields, you must drop the old index and create a new one. For large collections, dropping an index can be expensive because the server has to delete all index entries.
Actionable Takeaways
- Identify the most common lookup patterns in your application.
- For each pattern, create a sparse index on the relevant field(s).
- Use
Matchagainst the index in your FQL queries. - Monitor index population status and query performance in the Fauna console.
- Periodically review and drop indexes that are no longer needed.
By following these steps, you’ll keep your FaunaDB queries fast even as your data grows.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.