Zema Logo
Plugins

zema_firestore

Validate Cloud Firestore documents via withConverter. Every read is validated, every write converts DateTime to Timestamp.

Installation

dependencies:
  zema: ^0.5.0
  zema_firestore: ^0.1.0
  cloud_firestore: ^5.0.0
import 'package:zema/zema.dart';
import 'package:zema_firestore/zema_firestore.dart';

Quick start

final userSchema = z.object({
  'id': z.string(),
  'name': z.string().min(1),
  'email': z.string().email(),
  'createdAt': zTimestamp(),
});

final usersRef = FirebaseFirestore.instance
    .collection('users')
    .withZema(userSchema);

// Read: document ID is injected as 'id', Timestamp is converted to DateTime
final snapshot = await usersRef.doc('abc').get();
final user = snapshot.data(); // Map<String, dynamic> is validated

// Write: DateTime is converted to Timestamp automatically
await usersRef.doc('abc').set({
  'name': 'Alice',
  'email': 'alice@example.com',
  'createdAt': DateTime.now(),
});

// Query
final snap = await usersRef
    .orderBy('createdAt', descending: true)
    .limit(10)
    .get();

for (final doc in snap.docs) {
  print(doc.data()['name']);
}

Firebase-specific schemas

Use these instead of standard Zema primitives for Firestore-specific types:

final schema = z.object({
  'createdAt': zTimestamp(),   // Timestamp | DateTime  ->  DateTime
  'location': zGeoPoint(),     // GeoPoint
  'authorRef': zDocumentRef(), // DocumentReference
  'avatar': zBlob(),           // Blob
});

zTimestamp() accepts both Timestamp (from Firestore) and DateTime (from app code) and always produces a DateTime.

How it works

withZema(schema) calls Firestore's withConverter with a ZemaFirestoreConverter<T> that:

  1. Injects the document ID into the data map under 'id' (configurable).
  2. Runs schema.safeParse(data) on every read.
  3. Converts DateTime fields to Timestamp on every write.
  4. Throws ZemaFirestoreException on schema mismatch, or calls onParseError if provided.

Error handling

final usersRef = FirebaseFirestore.instance
    .collection('users')
    .withZema(
      userSchema,
      onParseError: (snapshot, error, stackTrace) {
        Sentry.captureException(error, stackTrace: stackTrace);
        return {
          'id': snapshot.id,
          'name': 'Unknown',
          'email': 'unknown@example.com',
          'createdAt': DateTime(2000),
        };
      },
    );

When no onParseError is provided, a ZemaFirestoreException is thrown with:

  • path: Firestore document path
  • documentId: document ID
  • issues: List<ZemaIssue> from Zema
  • receivedData: raw document data for debugging

Works with Query and DocumentReference

// Query
final active = FirebaseFirestore.instance
    .collection('users')
    .where('isActive', isEqualTo: true)
    .withZema(userSchema);

// DocumentReference
final ref = FirebaseFirestore.instance
    .collection('users')
    .doc('abc')
    .withZema(userSchema);

Configuration

ParameterTypeDefaultDescription
schemaZemaSchema<_, T>requiredSchema used to validate each document
validateWritesboolfalseValidate the map through schema before writing
injectDocumentIdbooltrueInject document ID into the data map before parsing
documentIdFieldString'id'Key used for the injected document ID
onParseErrorOnParseError<T>?nullFallback callback on parse failure
Copyright © 2026