Skip to content

Firestore Security Rules Data Validation — Ensuring Data Integrity at the Database Level

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Firestore Security Rules Data Validation. We cover key concepts, practical examples, and best practices to help you master this topic.

Firestore security rules provide built-in data validation capabilities that check document fields for correct types, required fields, value ranges, and format constraints before allowing writes.

What You'll Learn

  • Validating field types and required fields
  • Writing conditional rules for data integrity
  • Cross-document validation patterns

Why It Matters

Client-side validation is easily bypassed. Server-side validation in security rules ensures data integrity even from malicious clients. DodaTech uses security rules validation to enforce data quality across all client applications.

flowchart LR
    A["Write Request"] --> B{"Type validation"}
    B --> C{"Required fields"}
    C --> D{"Value constraints"}
    D --> E{"Cross-document checks"}
    E -->|"All pass"| F["Write allowed"]
    E -->|"Any fail"| G["Write rejected"]
    style F fill:#86efac,stroke:#16a34a
    style G fill:#fecaca,stroke:#dc2626

Code Examples

// Security rules with data validation
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    function isAuthenticated() {
      return request.auth != null;
    }

    // User document validation
    match /users/{userId} {
      allow read: if isAuthenticated();
      allow create: if isAuthenticated()
        && request.auth.uid == userId

        // Required fields exist and have correct types
        && request.resource.data.keys().hasAll([
          'name', 'email', 'createdAt'
        ])
        && request.resource.data.name is string
        && request.resource.data.name.size() > 0
        && request.resource.data.name.size() <= 100
        && request.resource.data.email is string
        && request.resource.data.email.matches(
          '^[^@]+@[^@]+\\.[^@]+$')
        && request.resource.data.createdAt is timestamp

        // Optional fields with constraints
        && (!request.resource.data.keys().hasAny(['role'])
          || request.resource.data.role in ['user', 'admin', 'moderator'])
        && (!request.resource.data.keys().hasAny(['age'])
          || (request.resource.data.age is number
            && request.resource.data.age >= 0
            && request.resource.data.age <= 150));

      allow update: if isAuthenticated()
        && request.auth.uid == userId

        // Cannot change email after creation
        && (!request.resource.data.keys().hasAny(['email'])
          || request.resource.data.email == resource.data.email)

        // Cannot change createdAt
        && !request.resource.data.keys().hasAny(['createdAt']);
    }

    // Product validation
    match /products/{productId} {
      allow write: if isAuthenticated()
        && request.resource.data.keys().hasAll([
          'name', 'price', 'category'
        ])
        && request.resource.data.name is string
        && request.resource.data.name.size() > 0
        && request.resource.data.price is number
        && request.resource.data.price >= 0
        && request.resource.data.category in [
          'electronics', 'software', 'services'
        ];
    }

    // Nested map validation
    match /orders/{orderId} {
      allow write: if isAuthenticated()
        && request.resource.data.items is list
        && request.resource.data.items.size() > 0
        && request.resource.data.items.size() <= 100
        && request.resource.data.total is number
        && request.resource.data.total >= 0;
    }
  }
}
// Cross-document validation
match /comments/{commentId} {
  allow create: if isAuthenticated()
    // Comment text validation
    && request.resource.data.text is string
    && request.resource.data.text.size() > 0
    && request.resource.data.text.size() <= 2000

    // Referenced post must exist
    && exists(
      /databases/$(database)/documents/posts/$(request.resource.data.postId)
    )

    // Comment count limiter
    && resource.data.commentCount < 1000;
}
# Test validation rules with emulator
firebase emulators:exec '
  firebase firestore:delete --all-collections -y
  # Run your test suite here
'

Common Mistakes

1. Not Validating Array Element Types

Security rules can validate arrays exist but not individual element types.

2. Overly Restrictive Rules That Block Valid Writes

Test rules thoroughly to ensure legitimate operations are not blocked.

3. Forgetting to Check for Field Presence

Use hasAll/hasAny to check required fields exist before validating their values.

4. Validating Fields That Don't Exist

Use conditionals to only validate fields when they are present.

5. Ignoring the Difference Between Create and Update

Create rules validate request.resource. Update rules may need different validation.

Practice Questions

  1. How do you check if a field has a specific type?
  2. How do you require certain fields to be present?
  3. How do you validate array sizes?
  4. Can you validate fields within nested maps?
  5. How do you validate fields only on create but not update?

Answers:

  1. Use the is keyword: request.resource.data.field is string.
  2. Use request.resource.data.keys().hasAll(['field1', 'field2']).
  3. Use request.resource.data.array.size() >= 1.
  4. Yes, but only top-level map types. Deeply nested validation is limited.
  5. Use separate allow create and allow update rules with different conditions.

Challenge: Write security rules for a blog platform that validate: post titles under 200 chars, body under 50000 chars, valid category references, comment text under 2000 chars, user display names alphanumeric only, and prevent changing author after creation.

FAQ

Can I validate the existence of referenced documents?

Yes. Use the exists() function to check if a document exists before allowing a write that references it.

How do I validate timestamp ranges?

Compare request.resource.data.timestamp against timestamp.value() or request.time for reasonable date ranges.

Can I use regex for field validation?

Yes. Use the matches() method with a regex pattern to validate string formats like email or phone numbers.

How do I prevent field deletion on update?

Check that required fields are present in request.resource.data using hasAll() to ensure they are not being removed.

What happens when validation fails?

The write operation is rejected with a PERMISSION_DENIED error. The client receives a FirestoreError with details about the failed rule.

Mini Project

Build a Firestore security rules validation suite for a user management system. Implement rules that validate: email format, username uniqueness (via document ID), age range, role enumeration, required fields on create, immutable fields on update, and referenced role documents exist.

What's Next

Learn about Firestore offline data persistence for mobile and web apps, then explore pagination with cursors for large datasets.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro