Skip to content
On this page

Version 4.0 Release Notes

Firestore ODM 4.0 builds on 3.0's performance foundation with a fully reworked code generator, broader type coverage, and ergonomic wins โ€” while keeping the schema, model, query, and update API you already write unchanged.

Breaking: cloud_firestore 6.x

4.0 requires cloud_firestore 6.x and firebase_core 4.x (was cloud_firestore 5.x). The ODM's own query/CRUD/update API is unchanged โ€” upgrade those two dependencies in your app and re-run the generator. See Upgrading below.

๐ŸŽ‰ What's New in 4.0

๐Ÿ”ข Enum support

Enums are now first-class. @JsonValue is honored for both string and numeric values, enums work in orderBy(), and defaults are generated.

dart
@JsonEnum()
enum AccountType {
  @JsonValue('free') free,
  @JsonValue('pro') pro,
  @JsonValue('enterprise') enterprise,
}

// Filter and order by enum fields, fully type-safe
final pros = await db.users
  .where(($) => $.accountType(isEqualTo: AccountType.pro))
  .orderBy(($) => $.accountType())
  .get();

๐Ÿงฉ Automatic nested-class imports

Filter, patch, aggregate, and orderBy selectors for nested types no longer require you to manually import the nested classes โ€” the generator wires up the imports for you.

dart
// `Profile` is nested inside `User`; no manual import of Profile needed
await db.users('jane').patch(($) => [
  $.profile.followers.increment(1),
  $.profile.bio.set('Updated bio'),
]);

๐Ÿ›ก๏ธ Stronger nullable & type handling

  • Nullable Map fields are supported in updates and filters.
  • Nested fromJson factories that accept a nullable map (e.g. Address.fromJson(Map<String, Object?>? json)) are now deserialized with a nullable cast, so reading a document where the field is absent no longer crashes with type 'Null' is not a subtype of type 'Map<String, Object?>' (#5).
  • Non-nullable enum fields resolve correctly (no spurious null assertions).

โฑ๏ธ Server timestamps on insert

FirestoreODM.serverTimestamp is now applied on insert operations (not only updates) and is honored inside batches.

๐Ÿงฑ Batch & transaction patch builders

Atomic patch operations are available inside runBatch and runTransaction, plus a getBatchCollection() convenience.

๐Ÿ—๏ธ Reworked code generator

The filter, patch, aggregate, and orderBy builders and the converters were rebuilt on a unified FieldPath model, with a consolidated TypeDefinition type. This is internal, but it produces cleaner generated code and a more consistent foundation for future features.

๐Ÿงช Firestore Pipelines (experimental)

collection.pipeline() returns a TypedPipeline<T> over Firestore's new Pipelines API โ€” built from the same type-safe $.field selector as the rest of the ODM (no string field paths). Row-preserving and shape-changing stages are all typed:

dart
// where / sort / limit -> List<User>
final adults = await db.users
    .pipeline()
    .where(($) => $.age(isGreaterThanOrEqualTo: 18))   // typed value; nested $.profile.x works
    .sort(($) => $.age.descending())
    .limit(20)
    .execute();

// select -> typed records
final rows = await db.users.pipeline()
    .select(($) => (name: $.name.value, years: $.age.value))
    .execute(); // -> List<({String name, int years})>

// aggregate -> a typed record
final stats = await db.users.pipeline()
    .aggregate(($) => (count: $.count(), avgAge: $.age.average())); // -> ({int count, double avgAge})

Pipelines are a Firestore Enterprise edition feature: one-shot execute() only (no realtime/offline), and not supported by the emulator or fake_cloud_firestore. This API is experimental: it is fully type-checked at compile time, but select/aggregate runtime behaviour must be validated against a real Enterprise database. Design + roadmap: ADRย 0001, #6.

โš™๏ธ Performance

The performance characteristics established in 3.0 are retained:

MetricValue
Runtime performance~20% faster than baseline
Generated code size~15% smaller
CompilationSub-second for complex schemas
Runtime overheadZero (all work at compile time)

โฌ†๏ธ Upgrading from 3.x

The ODM's own API is unchanged for typical usage (schema definition, models, queries, patch/modify, transactions, batches). The one required change is the cloud_firestore 6.x / firebase_core 4.x bump in your app:

bash
dart pub add firestore_odm:^4.0.0
dart pub add dev:firestore_odm_builder:^4.0.0
dart pub add cloud_firestore:^6.4.0
dart pub add firebase_core:^4.0.0
dart run build_runner build --delete-conflicting-outputs

cloud_firestore 6.0 removed the deprecated persistence Settings toggles โ€” use Settings.localCache if you configured those. If you depended on ODM internal-only classes (e.g. the removed BatchField / BatchConfiguration helpers, or the internal Node2 type), prefer the public collection/document/patch-builder APIs instead.

๐Ÿ”ญ What's next

  • Firestore Pipelines: promote out of experimental once validated on Enterprise; add per-model typed selectors and the remaining stages
  • Full map field filtering, ordering, and aggregation
  • Nested map support