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:
- Injects the document ID into the data map under
'id'(configurable). - Runs
schema.safeParse(data)on every read. - Converts
DateTimefields toTimestampon every write. - Throws
ZemaFirestoreExceptionon schema mismatch, or callsonParseErrorif 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 pathdocumentId: document IDissues:List<ZemaIssue>from ZemareceivedData: 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
| Parameter | Type | Default | Description |
|---|---|---|---|
schema | ZemaSchema<_, T> | required | Schema used to validate each document |
validateWrites | bool | false | Validate the map through schema before writing |
injectDocumentId | bool | true | Inject document ID into the data map before parsing |
documentIdField | String | 'id' | Key used for the injected document ID |
onParseError | OnParseError<T>? | null | Fallback callback on parse failure |