{"id":32286333,"url":"https://github.com/sylphxltd/firestore_odm","last_synced_at":"2025-10-23T02:00:19.019Z","repository":{"id":297756114,"uuid":"997772820","full_name":"sylphxltd/firestore_odm","owner":"sylphxltd","description":"A type-safe, annotation-based Firestore ODM for Dart and Flutter. Code-generation-first design with no reflection, built for clean architecture and performance.","archived":false,"fork":false,"pushed_at":"2025-08-09T03:21:44.000Z","size":21394,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-23T01:59:57.243Z","etag":null,"topics":["dart","database","firebase","firestore","flutter","flutterfire","freezed","odm","riverpod"],"latest_commit_sha":null,"homepage":"https://sylphxltd.github.io/firestore_odm/","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sylphxltd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-07T06:33:52.000Z","updated_at":"2025-10-06T13:11:58.000Z","dependencies_parsed_at":"2025-06-07T09:25:59.023Z","dependency_job_id":"0da57c99-aa00-4b08-88a2-a2987836caee","html_url":"https://github.com/sylphxltd/firestore_odm","commit_stats":null,"previous_names":["sylphxltd/firestore_odm"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/sylphxltd/firestore_odm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sylphxltd%2Ffirestore_odm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sylphxltd%2Ffirestore_odm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sylphxltd%2Ffirestore_odm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sylphxltd%2Ffirestore_odm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sylphxltd","download_url":"https://codeload.github.com/sylphxltd/firestore_odm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sylphxltd%2Ffirestore_odm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280546417,"owners_count":26348724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-23T02:00:06.710Z","response_time":142,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","database","firebase","firestore","flutter","flutterfire","freezed","odm","riverpod"],"created_at":"2025-10-23T02:00:16.843Z","updated_at":"2025-10-23T02:00:18.976Z","avatar_url":"https://github.com/sylphxltd.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Firestore ODM for Dart/Flutter\n\n**Stop fighting with Firestore queries. Start building amazing apps.**\n\nTransform your Firestore development experience with type-safe, intuitive database operations that feel natural and productive.\n\n[![pub package](https://img.shields.io/pub/v/firestore_odm.svg)](https://pub.dev/packages/firestore_odm)\n[![GitHub](https://img.shields.io/github/license/sylphxltd/firestore_odm)](https://github.com/sylphxltd/firestore_odm/blob/main/LICENSE)\n\n## 🎉 New in Version 3.0!\n\n**The most stable and feature-complete release yet** - over 90% of planned features are now complete!\n\n### ⚡ Major Performance Improvements\n- **20% faster runtime performance** with optimized code generation\n- **15% less generated code** through smart extension-based architecture\n- **Lightning-fast generation** - complex schemas compile in under 1 second\n- **Inline-first approach** for maximum efficiency\n\n### 🚀 New Features \u0026 Capabilities\n- **Full generic model support** - Generic classes with type-safe patch operations\n- **Complete JsonKey \u0026 JsonConverter support** - Full control over serialization\n- **Automatic conversion fallbacks** - JsonConverter no longer required in most cases\n- **Enhanced map operations** - Comprehensive map field support with atomic operations\n\n### 🛡️ Stability \u0026 Quality\n- **100+ new test cases** added for comprehensive coverage\n- **Major bug fixes** including map clear, map set, and nested operations\n- **Production-ready stability** with rigorous testing\n\n### 📋 Known Limitations (3.0)\n- Map fields don't support nested maps or special symbols in keys\n- Batch collection operations (coming soon)\n- Map field filtering, ordering, and aggregation (planned)\n\n##  Complete Documentation\n\n**[📚 Read the Full Documentation](https://sylphxltd.github.io/firestore_odm/)** - Comprehensive guides, examples, and API reference\n\n## 📋 Table of Contents\n\n- [Why Firestore ODM?](#-why-firestore-odm)\n- [Before vs After](#-before-vs-after)\n- [Key Features](#-key-features)\n- [Quick Start](#-quick-start)\n- [Advanced Features](#-advanced-features)\n- [Performance \u0026 Technical Excellence](#-performance--technical-excellence)\n- [Testing](#-testing)\n- [Comparison with Standard Firestore](#-comparison-with-standard-firestore)\n- [Contributing](#-contributing)\n- [License](#-license)\n\n## 🚀 Why Firestore ODM?\n\nIf you've worked with Flutter and Firestore, you know the pain:\n\n- **No Type Safety** - String-based field paths that break at runtime, not compile time\n- **Manual Serialization** - Converting `DocumentSnapshot` to models and back is tedious and error-prone\n- **Complex Queries** - Writing nested logical queries is difficult and hard to read\n- **Runtime Errors** - Typos in field names cause crashes in production\n- **Incomplete Solutions** - Other ODMs are often incomplete or not actively maintained\n\nWe built Firestore ODM to solve these problems with:\n- ✅ **Complete type safety** throughout your entire data layer\n- ✅ **Lightning-fast code generation** using callables and Dart extensions\n- ✅ **Minimal generated code** that doesn't bloat your project\n- ✅ **Model reusability** across collections and subcollections\n- ✅ **Revolutionary features** like Smart Builder pagination and streaming aggregations\n- ✅ **Zero runtime overhead** - all magic happens at compile time\n\n## 🔥 Before vs After\n\n### Type Safety Revolution\n```dart\n// ❌ Standard cloud_firestore - Runtime errors waiting to happen\nDocumentSnapshot doc = await FirebaseFirestore.instance\n  .collection('users')\n  .doc('user123')\n  .get();\n\nMap\u003cString, dynamic\u003e? data = doc.data() as Map\u003cString, dynamic\u003e?;\nString name = data?['name']; // Runtime error if field doesn't exist\nint age = data?['profile']['age']; // Nested access is fragile\n```\n\n```dart\n// ✅ Firestore ODM - Compile-time safety\nUser? user = await db.users('user123').get();\nString name = user.name; // IDE autocomplete, compile-time checking\nint age = user.profile.age; // Type-safe nested access\n```\n\n### Smart Query Building\n```dart\n// ❌ Standard - String-based field paths, typos cause runtime errors\nfinal result = await FirebaseFirestore.instance\n  .collection('users')\n  .where('isActive', isEqualTo: true)\n  .where('profile.followers', isGreaterThan: 100)\n  .where('age', isLessThan: 30)\n  .get();\n```\n\n```dart\n// ✅ ODM - Type-safe query builder with IDE support\nfinal result = await db.users\n  .where(($) =\u003e $.and(\n    $.isActive(isEqualTo: true),\n    $.profile.followers(isGreaterThan: 100),\n    $.age(isLessThan: 30),\n  ))\n  .get();\n```\n\n### Intelligent Updates\n```dart\n// ❌ Standard - Manual map construction, error-prone\nawait userDoc.update({\n  'profile.followers': FieldValue.increment(1),\n  'tags': FieldValue.arrayUnion(['verified']),\n  'lastLogin': FieldValue.serverTimestamp(),\n});\n```\n\n```dart\n// ✅ ODM - Two powerful update strategies\n\n// 1. Patch - Explicit atomic operations (Best Performance)\nawait userDoc.patch(($) =\u003e [\n  $.profile.followers.increment(1),\n  $.tags.add('verified'),              // Add single element\n  $.tags.addAll(['premium', 'active']), // Add multiple elements\n  $.scores.removeAll([0, -1]),         // Remove multiple elements\n  $.lastLogin.serverTimestamp(),\n]);\n\n// 2. Modify - Smart atomic detection (Read + Auto-detect operations)\nawait userDoc.modify((user) =\u003e user.copyWith(\n  age: user.age + 1,              // Auto-detects -\u003e FieldValue.increment(1)\n  tags: [...user.tags, 'expert'], // Auto-detects -\u003e FieldValue.arrayUnion()\n  lastLogin: FirestoreODM.serverTimestamp,\n));\n```\n\n## ⚡ Key Features\n\n### 🛡️ Complete Type Safety\n- **No `Map\u003cString, dynamic\u003e`** anywhere in your code\n- **Compile-time field validation** - typos become build errors, not runtime crashes\n- **IDE autocomplete** for all database operations\n- **Strong typing** for nested objects, generics, and complex data structures\n\n### 🚀 Lightning Fast Code Generation (3.0 Enhanced)\n- **Inline-first optimized** generated code using callables and Dart extensions\n- **15% less generated code** - smart generation without bloating your project\n- **20% performance improvement** - optimized runtime execution\n- **Model reusability** - same model works in collections and subcollections\n- **Sub-second generation** - complex schemas compile in under 1 second\n- **Zero runtime overhead** - all magic happens at compile time\n\n### 🧬 Advanced Generic Support (New in 3.0)\n- **Full generic model support** - Type-safe generic classes and nested types\n- **Generic patch operations** - Atomic operations that respect generic type constraints\n- **JsonKey \u0026 JsonConverter support** - Complete control over field serialization\n- **Automatic conversion fallbacks** - Smart type conversion when converters aren't defined\n\n### 🧠 Revolutionary Pagination\nOur **Smart Builder** eliminates the most common Firestore pagination bugs:\n```dart\n// Get first page with ordering\nfinal page1 = await db.users\n  .orderBy(($) =\u003e ($.followers(descending: true), $.name()))\n  .limit(10)\n  .get();\n\n// Get next page with perfect type-safety - zero inconsistency risk\n// The same orderBy ensures cursor consistency automatically\nfinal page2 = await db.users\n  .orderBy(($) =\u003e ($.followers(descending: true), $.name()))\n  .startAfterObject(page1.last) // Auto-extracts cursor values\n  .limit(10)\n  .get();\n```\n\n### 📊 Streaming Aggregations (Unique Feature!)\nReal-time aggregation subscriptions that Firestore doesn't support natively:\n```dart\n// Live statistics that update in real-time\ndb.users\n  .where(($) =\u003e $.isActive(isEqualTo: true))\n  .aggregate(($) =\u003e (\n    count: $.count(),\n    averageAge: $.age.average(),\n    totalFollowers: $.profile.followers.sum(),\n  ))\n  .stream\n  .listen((stats) {\n    print('Live: ${stats.count} users, avg age ${stats.averageAge}');\n  });\n```\n\n### 🏦 Smart Transactions\nAutomatic **deferred writes** handle Firestore's read-before-write rule:\n```dart\nawait db.runTransaction((tx) async {\n  // All reads happen first automatically\n  final sender = await tx.users('user1').get();\n  final receiver = await tx.users('user2').get();\n  \n  // Writes are automatically deferred until the end\n  tx.users('user1').patch(($) =\u003e [$.balance.increment(-100)]);\n  tx.users('user2').patch(($) =\u003e [$.balance.increment(100)]);\n});\n```\n\n### ⚡ Atomic Batch Operations\nPerform multiple writes atomically with two convenient approaches:\n```dart\n// Automatic management - simple and clean\nawait db.runBatch((batch) {\n  batch.users.insert(newUser);\n  batch.posts.update(existingPost);\n  batch.users('user_id').posts.insert(userPost);\n  batch.users('old_user').delete();\n});\n\n// Manual management - fine-grained control\nfinal batch = db.batch();\nbatch.users.insert(user1);\nbatch.users.insert(user2);\nbatch.posts.update(post);\nawait batch.commit();\n```\n\n### 🔗 Flexible Data Modeling\nSupport for multiple modeling approaches:\n- **`freezed`** (recommended) - Robust immutable classes\n- **`json_serializable`** - Plain Dart classes with full control\n- **`fast_immutable_collections`** - High-performance `IList`, `IMap`, `ISet`\n\n### 🏗️ Schema-Based Architecture\n- **Multiple ODM instances** for different app modules\n- **Compile-time validation** of collection paths and relationships\n- **Automatic subcollection detection** and type-safe access\n- **Clean separation** of database concerns\n\n## 🚀 Quick Start\n\n### 1. Installation\n\nInstall Firestore ODM:\n\n```bash\ndart pub add firestore_odm\ndart pub add dev:firestore_odm_builder\ndart pub add dev:build_runner\n```\n\nYou'll also need a JSON serialization solution:\n\n```bash\n# If using Freezed\ndart pub add freezed_annotation\ndart pub add dev:freezed\ndart pub add dev:json_serializable\n\n# If using plain classes\ndart pub add json_annotation\ndart pub add dev:json_serializable\n```\n\n### 2. Configure json_serializable (Critical for Nested Models)\n\n**⚠️ Important:** If you're using models with nested objects (especially with Freezed), you **must** create a `build.yaml` file next to your `pubspec.yaml`:\n\n```yaml\n# build.yaml\ntargets:\n  $default:\n    builders:\n      json_serializable:\n        options:\n          explicit_to_json: true\n```\n\n**Why is this required?** Without this configuration, `json_serializable` generates broken `toJson()` methods for nested objects. Instead of proper JSON, you'll get `Instance of 'NestedClass'` stored in Firestore, causing data corruption and deserialization failures.\n\n**When you need this:**\n- ✅ Using nested Freezed classes\n- ✅ Using nested objects with `json_serializable`\n- ✅ Working with complex object structures\n- ✅ Encountering \"Instance of...\" in Firestore console\n\n**Alternative:** Add `@JsonSerializable(explicitToJson: true)` to individual classes if you can't use global configuration.\n\n### 3. Define Your Model\n```dart\n// lib/models/user.dart\nimport 'package:firestore_odm_annotation/firestore_odm_annotation.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'user.freezed.dart';\npart 'user.g.dart';\n\n@freezed\nclass User with _$User {\n  const factory User({\n    @DocumentIdField() required String id,\n    required String name,\n    required String email,\n    required int age,\n    DateTime? lastLogin,\n  }) = _User;\n\n  factory User.fromJson(Map\u003cString, dynamic\u003e json) =\u003e _$UserFromJson(json);\n}\n```\n\n### 4. Define Your Schema\n```dart\n// lib/schema.dart\nimport 'package:firestore_odm_annotation/firestore_odm_annotation.dart';\nimport 'models/user.dart';\n\npart 'schema.odm.dart';\n\n@Schema()\n@Collection\u003cUser\u003e(\"users\")\nfinal appSchema = _$AppSchema;\n```\n\n### 5. Generate Code\n```bash\ndart run build_runner build --delete-conflicting-outputs\n```\n\n### 6. Start Using\n```dart\nimport 'package:cloud_firestore/cloud_firestore.dart';\nimport 'package:firestore_odm/firestore_odm.dart';\nimport 'schema.dart';\n\nfinal firestore = FirebaseFirestore.instance;\nfinal db = FirestoreODM(appSchema, firestore: firestore);\n\n// Create a user with custom ID\nawait db.users.insert(User(\n  id: 'jane',\n  name: 'Jane Smith',\n  email: 'jane@example.com',\n  age: 28,\n));\n\n// Create a user with auto-generated ID\nawait db.users.insert(User(\n  id: FirestoreODM.autoGeneratedId,\n  name: 'John Doe',\n  email: 'john@example.com',\n  age: 30,\n));\n\n// Get a user\nfinal user = await db.users('jane').get();\nprint(user?.name); // \"Jane Smith\"\n\n// Type-safe queries\nfinal youngUsers = await db.users\n  .where(($) =\u003e $.age(isLessThan: 30))\n  .orderBy(($) =\u003e $.name())\n  .get();\n```\n\n## 🌟 Advanced Features\n\n### Subcollections with Model Reusability\n```dart\n@Schema()\n@Collection\u003cUser\u003e(\"users\")\n@Collection\u003cPost\u003e(\"posts\")\n@Collection\u003cPost\u003e(\"users/*/posts\") // Same Post model, different location\nfinal appSchema = _$AppSchema;\n\n// Access user's posts\nfinal userPosts = db.users('jane').posts;\nawait userPosts.insert(Post(id: 'post1', title: 'Hello World!'));\n```\n\n### Bulk Operations\n```dart\n// Update all premium users using patch (best performance)\nawait db.users\n  .where(($) =\u003e $.isPremium(isEqualTo: true))\n  .patch(($) =\u003e [$.points.increment(100)]);\n\n// Update all premium users using modify (read + auto-detect atomic)\nawait db.users\n  .where(($) =\u003e $.isPremium(isEqualTo: true))\n  .modify((user) =\u003e user.copyWith(points: user.points + 100));\n\n// Delete inactive users\nawait db.users\n  .where(($) =\u003e $.status(isEqualTo: 'inactive'))\n  .delete();\n```\n\n### Server Timestamps \u0026 Auto-Generated IDs\n```dart\n// Server timestamps using patch (best performance)\nawait userDoc.patch(($) =\u003e [$.lastLogin.serverTimestamp()]);\n\n// Server timestamps using modify (read + smart detection)\nawait userDoc.modify((user) =\u003e user.copyWith(\n  loginCount: user.loginCount + 1,  // Uses current value + auto-detects increment\n  lastLogin: FirestoreODM.serverTimestamp,\n));\n\n// ⚠️ IMPORTANT: Server timestamp arithmetic doesn't work\n// ❌ This creates a regular DateTime, NOT a server timestamp:\n// FirestoreODM.serverTimestamp + Duration(days: 1)\n\n// Auto-generated document IDs\nawait db.users.insert(User(\n  id: FirestoreODM.autoGeneratedId, // Server generates unique ID\n  name: 'John Doe',\n  email: 'john@example.com',\n));\n```\n\n**⚠️ Server Timestamp Warning:** `FirestoreODM.serverTimestamp` must be used exactly as-is. Any arithmetic operations (`+`, `.add()`, etc.) will create a regular `DateTime` instead of a server timestamp. See the [Server Timestamps Guide](https://sylphxltd.github.io/firestore_odm/guide/server-timestamps.html) for alternatives.\n\n## 📊 Performance \u0026 Technical Excellence (3.0 Enhanced)\n\n### Optimized Code Generation\n- **Inline-first architecture** with callables and Dart extensions for maximum performance\n- **15% reduction in generated code** - smart generation without project bloat\n- **20% runtime performance improvement** - optimized execution paths\n- **Sub-second compilation** - complex schemas generate in under 1 second\n- **Zero runtime overhead** - all magic happens at compile time\n\n### Advanced Query Capabilities\n- **Complex logical operations** with `and()` and `or()`\n- **Array operations** - `arrayContains`, `arrayContainsAny`, `whereIn`\n- **Range queries** with proper ordering constraints\n- **Nested field access** with full type safety\n\n### Real-world Ready\n- **Transaction support** with automatic deferred writes\n- **Streaming subscriptions** for real-time updates\n- **Error handling** with meaningful compile-time messages\n- **Testing support** with `fake_cloud_firestore` integration\n\n## 🧪 Testing\n\nPerfect integration with `fake_cloud_firestore`:\n```dart\nimport 'package:fake_cloud_firestore/fake_cloud_firestore.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nvoid main() {\n  test('user operations work correctly', () async {\n    final firestore = FakeFirebaseFirestore();\n    final db = FirestoreODM(appSchema, firestore: firestore);\n\n    await db.users.insert(User(id: 'test', name: 'Test User', email: 'test@example.com', age: 25));\n\n    final user = await db.users('test').get();\n    expect(user?.name, 'Test User');\n  });\n}\n```\n\n## 📈 Comparison with Standard Firestore\n\n| Feature | Standard cloud_firestore | Firestore ODM |\n|---------|-------------------------|---------------|\n| **Type Safety** | ❌ Map\u003cString, dynamic\u003e everywhere | ✅ Strong types throughout |\n| **Query Building** | ❌ String-based, error-prone | ✅ Type-safe with IDE support |\n| **Data Updates** | ❌ Manual map construction | ✅ Two powerful update strategies |\n| **Generic Support** | ❌ No generic handling | ✅ Full generic model support |\n| **Aggregations** | ❌ Basic count only | ✅ Comprehensive + streaming |\n| **Pagination** | ❌ Manual, inconsistency risks | ✅ Smart Builder, zero risk |\n| **Transactions** | ❌ Manual read-before-write | ✅ Automatic deferred writes |\n| **Code Generation** | ❌ None | ✅ Inline-optimized, 15% smaller |\n| **Model Reusability** | ❌ N/A | ✅ Same model, multiple collections |\n| **Runtime Errors** | ❌ Common | ✅ Eliminated at compile-time |\n| **Developer Experience** | ❌ Frustrating | ✅ Productive and enjoyable |\n\n## 🤝 Contributing\n\nWe love contributions! See our [Contributing Guide](CONTRIBUTING.md) for details.\n\n## 📄 License\n\nMIT License - see [LICENSE](LICENSE) file for details.\n\n---\n\n**Ready to transform your Firestore experience?** \n\n🔗 **[Get Started Now](https://sylphxltd.github.io/firestore_odm/guide/getting-started.html)** | 📚 **[Full Documentation](https://sylphxltd.github.io/firestore_odm/)** | 🐛 **[Report Issues](https://github.com/sylphxltd/firestore_odm/issues)**\n\nBuild type-safe, maintainable Flutter apps with the power of Firestore ODM! 🚀","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsylphxltd%2Ffirestore_odm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsylphxltd%2Ffirestore_odm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsylphxltd%2Ffirestore_odm/lists"}