Skip to content
Back to Interview Guides
Interview Guide

20 Advanced Firebase Interview Questions for Experienced Developers

· 12 min read
Firebase Q&A Component

Jump to Category

Firestore: Data Modeling & Queries ️ Security Rules
☁️ Cloud Functions ️ Architecture & Best Practices
Firebase Authentication

Firestore: Data Modeling & Queries

1. When would you choose Firestore over the Realtime Database, and vice versa?

  • Choose **Firestore** for most new applications. It has a more powerful and scalable query model (compound queries), supports multi-region deployments for higher availability, has a more predictable pricing model, and offers a richer feature set. It’s ideal for general-purpose apps, e-commerce, and SaaS products.
  • Choose **Realtime Database (RTDB)** when your primary requirement is extremely low-latency state synchronization. RTDB is a single, massive JSON tree, making it incredibly fast for simple state changes that need to be reflected across clients instantly. It’s great for applications like collaborative whiteboards or simple presence indicators where query complexity is low.
Read the official documentation comparing the two databases.

2. Explain denormalization in Firestore. Why is it a necessary pattern?

**Denormalization** is the practice of storing redundant copies of data across multiple documents or collections to optimize for read performance. Firestore does not support server-side joins, so to get related data, you would have to make multiple round trips to the database.

It’s a necessary pattern because it allows you to design your database around your app’s specific query needs. For example, instead of storing a `user_id` on a post and then fetching the user’s details separately, you would store a copy of the `author_name` and `author_avatar` directly on the post document. This allows you to fetch a list of posts and display them with author information in a single, efficient query.

Read the guide on structuring data in Firestore.

3. What is a composite index in Firestore and when do you need to create one?

A composite index is an index on multiple fields in a collection. You need to create one manually when you want to perform a query that involves range (`<`, `>`) or `array-contains` clauses on one field and equality (`==`) or additional range clauses on other fields.

For example, a query like `collection.where(‘status’, ‘==’, ‘active’).where(‘created_at’, ‘>’, some_timestamp)` would require a composite index on `(status, created_at)`. Firestore will provide an error message with a direct link to create the required index if one is missing.

Learn about managing indexes in Firestore.

4. How do you implement pagination in Firestore?

Firestore uses cursor-based pagination. You cannot use a simple offset.

The process is:

  1. Perform an initial query with a `limit()` clause.
  2. Save the last document from the result set.
  3. To fetch the next page, you use the `startAfter()` cursor method, passing the saved document snapshot from the previous query. For example: `query.orderBy(‘population’).startAfter(lastVisible).limit(25)`.

This method is efficient and stable, even if data is added or removed between page fetches.

5. What is the difference between a transaction and a batched write?

  • A **Batched Write** is a set of write operations (create, update, delete) that are executed as a single atomic unit. If any operation fails, none are applied. It’s for performing multiple writes without needing to read any data first.
  • A **Transaction** is more powerful. It’s a set of read and write operations. A transaction will read some documents first, and if any of those documents are modified by another process before the transaction commits its writes, the entire transaction will fail and can be retried. It’s used when your writes depend on the existing state of one or more documents (a read-modify-write operation).

Security Rules

6. What is the difference between `get()` and `exists()` in Firestore Security Rules? Why is this important for billing?

  • `get()`: Reads the full document from the specified path. It returns the document’s data.
  • `exists()`: Only checks for the existence of a document at the specified path. It returns `true` or `false` and does not return the document’s content.

This is critical for billing because both `get()` and `exists()` count as **one read operation**. However, if your rule only needs to check if a document is present (e.g., to see if a user exists in a specific collection), using `exists()` is much more secure. `get()` would expose the entire document’s data to the rule’s context, which could be accidentally used or leaked in subsequent rule evaluations. You should always prefer `exists()` for existence checks.

Read the documentation on accessing other documents in security rules.

7. How can you write a security rule that allows a user to only read or write their own data?

You would structure your data so that each user’s document has their `uid` as its document ID. Then, in your security rules, you can match the incoming request’s `auth.uid` with the `userId` in the document path.

Example rule for a `users` collection:

match /users/{userId} {
  allow read, write: if request.auth.uid == userId;
}

This ensures that an authenticated user can only access the document whose ID matches their own user ID.

8. Explain how security rules are *not* filters.

This is a fundamental concept. Security rules are for authorization, not for filtering data. When a client performs a query, Firestore first determines the *potential* result set based on the query’s filters. Then, it checks the security rules against *every single document* in that potential result set. If even one document fails the security rule check, the **entire query fails**.

This means your client-side queries must always include the same constraints that your security rules enforce. For example, if your rule is `allow read: if resource.data.ownerId == request.auth.uid;`, your client query *must* include `.where(‘ownerId’, ‘==’, currentUser.uid)`.

9. How would you implement role-based access control (RBAC) using security rules?

The standard way is to use **Firebase Auth custom claims**. When a user is created or their role is changed, you would use a backend process (like a Cloud Function) to set a custom claim on their auth token, e.g., `{ role: ‘admin’ }`.

This claim is then available in your security rules via `request.auth.token`. You can write rules like:

allow write: if request.auth.token.role == 'admin';

This is secure because custom claims can only be set from a trusted server environment.

Learn about controlling access with Custom Claims.

10. What are function declarations in security rules?

Functions allow you to package and reuse common conditions across your security rules, making them cleaner and more maintainable. You can define a function at the top level of your rules file and then call it within your `allow` expressions.

Example:

function isSignedIn() {
  return request.auth != null;
}
match /posts/{postId} {
  allow read: if isSignedIn();
}

Cloud Functions

11. What is an idempotent Cloud Function and why is it important?

An **idempotent** function is one that can be run multiple times with the same input but will only produce the result once. This is critical because many event-driven triggers in Firebase (like Pub/Sub or even some Firestore triggers in case of retries) have an **”at-least-once”** delivery guarantee, meaning your function might be invoked more than once for the same event.

To make a function idempotent, you must track the IDs of events you’ve already processed in a persistent store (like Firestore or Redis). When the function is triggered, it first checks if the event ID has been processed. If so, it exits gracefully. If not, it processes the event and then records the event ID.

Read the official tip on using idempotency.

12. Compare callable functions and HTTPS functions.

  • HTTPS Functions: Are generic webhooks that can be called from anywhere via an HTTP request. You are responsible for parsing the request body, handling CORS, and authenticating the user (e.g., by validating an ID token from the `Authorization` header).
  • Callable Functions: Are a specialized type of HTTPS function designed to be called directly from a Firebase client SDK. The SDK handles passing the user’s authentication token, serializing data, and managing CORS automatically. They provide a much simpler and more secure way to call a function from your client app.

13. How would you manage secrets and API keys in Cloud Functions?

You should never hardcode secrets in your source code. The recommended approach is to use the built-in secrets management functionality, which is integrated with **Google Cloud Secret Manager**. You can define a secret using the CLI, and then expose it to your function at runtime. The function’s underlying service account is granted permission to access the secret. This keeps secrets out of your code and environment variables and provides a secure, managed lifecycle for them.

14. What are some strategies for reducing the cold start time of a Cloud Function?

Strategies include:

  • Set a minimum number of instances: This keeps a specified number of function instances warm and ready, eliminating cold starts for them at a cost.
  • Minimize dependencies: Keep your deployment package small.
  • Lazy initialization: Initialize global variables and connections lazily (the first time they are needed) rather than in the global scope, which runs during the cold start.
  • Use a faster language: Node.js and Go generally have faster cold start times than Java.

Architecture & Best Practices

15. How would you implement a full-text search solution for a Firestore collection?

Firestore does not support native full-text search. The standard approach is to use a dedicated third-party search service like **Algolia** or **Typesense**.

The architecture would be:

  1. Use a Cloud Function that triggers on any write (`onCreate`, `onUpdate`, `onDelete`) to your Firestore collection.
  2. This function takes the document data and syncs it to your search service’s index.
  3. Your client application then queries the search service directly to get a list of matching document IDs.
  4. Finally, the client uses those IDs to fetch the full documents directly from Firestore.

16. What is the “fan-out” pattern in the context of the Realtime Database?

The fan-out pattern is a denormalization strategy used to optimize reads in the Realtime Database. Because RTDB queries are deep (reading a node also reads its entire subtree), you want to keep your data structure as flat as possible.

For example, instead of nesting a user’s posts under their user object, you would have a top-level `/posts` node and a separate `/users/$uid/posts` node that just contains the IDs of their posts. When a user creates a new post, you perform a multi-path update to write the full post to `/posts` and just the post ID to the user’s list. This allows you to fetch a user’s list of post IDs without downloading every full post.

Read about fanning out your data.

17. What is the purpose of the Firebase Emulator Suite?

The Emulator Suite provides high-fidelity local emulators for most Firebase services, including Firestore, Realtime Database, Auth, and Cloud Functions. Its purpose is to enable a fast, secure, and free local development loop.

It allows you to test your application’s logic, including security rules and function triggers, entirely on your local machine without incurring costs or affecting production data. It’s an essential tool for building robust CI/CD pipelines for Firebase applications.

Explore the Firebase Emulator Suite documentation.

18. How would you design a system to perform aggregations (like `SUM` or `COUNT`) on a large Firestore collection?

Firestore doesn’t have native server-side aggregation queries. The recommended approach is to perform the aggregations incrementally.

  • Simple Counters: For something like a “like” count, you can use a distributed counter approach with multiple documents (“shards”) to avoid write contention on a single document.
  • Complex Aggregations: Use a Cloud Function that triggers on every write to the collection. This function would read the change and atomically update an aggregation document stored in a separate collection. For example, on every new `sale` document, a function would read the sale amount and add it to a `daily_sales_total` document.

Firebase Authentication

19. What are Firebase Auth custom claims and what is their primary use case?

Custom claims are key-value pairs that you can set on a user’s account from a trusted backend environment (e.g., via the Admin SDK). These claims are then included in the user’s ID token.

Their primary use case is implementing **role-based access control (RBAC)**. You can set a claim like `{“role”: “admin”}` or `{“premium”: true}`. This information is then available in your Security Rules (`request.auth.token.role`) or on your backend server to make authorization decisions without needing an extra database lookup.

20. What is App Check and how does it improve security?

App Check is a security feature that protects your backend resources (like Firestore and Cloud Functions) from abuse by ensuring that requests originate from your authentic app or website, and not from a malicious or unauthorized client.

It works by using an “attestation provider” (like App Attest on iOS, Play Integrity on Android, or reCAPTCHA on web) to verify the client’s authenticity. The client then gets a short-lived App Check token, which it sends with every backend request. Your backend resources can then be configured to reject any request that doesn’t have a valid App Check token.

Skip the interview marathon.

We pre-vet senior engineers across Asia using these exact questions and more. Get matched in 24 hours, $0 upfront.

Get Pre-Vetted Talent
WhatsApp