Firebase Repository Implementation

A Flutter package that provides a clean repository interface for Firebase Firestore, following the KISS (Keep It Simple, Stupid) principle.

Overview

This package implements the kiss_repository interface for Firebase Firestore, providing type-safe CRUD operations, real-time streaming, and flexible query building. It's designed specifically for Flutter applications that need Firestore integration with a clean, consistent API.

✨ Features & Limitations

✅ Standard Repository Features

  • ✅ Complete CRUD operations (Create, Read, Update, Delete)
  • ✅ Batch operations for multiple items
  • ✅ Type-safe data conversions between Dart and backend
  • ✅ Custom query building with QueryBuilder
  • ✅ Built-in error handling with typed exceptions

🔥 Firebase-Specific Features

  • Real-time streaming with Firestore listeners
  • Multi-instance streaming - Works across multiple server instances/deployments
  • ✅ Offline support with automatic local caching and sync
  • ✅ Auto-generated IDs (using Firestore's ID generation)
  • ✅ Firebase emulator support for development
  • ✅ Integration with Firebase ecosystem

📡 Streaming Architecture

  • Server-Side Streaming: True database-level streaming with Firestore listeners
  • Multi-Instance Support: Changes from any server instance streamed to all connected clients
  • Real-time Sync: Automatic synchronization across web, mobile, desktop, and server environments
  • Perfect for: Multi-server web applications, real-time collaborative apps
  • Horizontal Scaling: Native support for distributed deployments
  • Offline Sync: Automatic local caching and synchronization when reconnected

⚠️ Limitations

  • Prefix-only search: Only supports prefix matching ("Fire" finds "Firebase", but "base" won't)
  • Case-sensitive search: "fire" won't find "Firebase" (case matters)
  • Non-atomic batch updates: updateAll processes valid items and skips invalid ones (not truly atomic)
  • Complex queries: Subject to Firestore's query limitations
  • Flutter only: Requires Flutter SDK (not pure Dart)

🚀 Quick Start

Prerequisites

  • Flutter SDK ^3.6.0
  • Firebase CLI (for local development with emulator)
  • Android SDK with emulator (for integration tests)
  • Firebase project setup

Installation

Add this package to your Flutter project:

dart pub add kiss_firebase_repository

Basic Usage

import 'package:kiss_firebase_repository/kiss_firebase_repository.dart';

// 1. Define your model
class User {
  final String id;
  final String name;
  final DateTime createdAt;

  User({required this.id, required this.name, required this.createdAt});
  
  User copyWith({String? id, String? name, DateTime? createdAt}) {
    return User(
      id: id ?? this.id,
      name: name ?? this.name,
      createdAt: createdAt ?? this.createdAt,
    );
  }
}

// 2. Create repository
final userRepository = RepositoryFirestore<User>(
  path: 'users',
  toFirestore: (user) => {
    'name': user.name,
    'createdAt': user.createdAt,
  },
  fromFirestore: (ref, data) => User(
    id: ref.id,
    name: data['name'],
    createdAt: data['createdAt'],
  ),
);

// 3. Use it
final newUser = await userRepository.add(
  IdentifedObject('user123', User(
    id: 'user123',
    name: 'John Doe',
    createdAt: DateTime.now(),
  )),
);

🔧 Development Setup

Firebase Emulator Setup

# Start Firebase emulator
./scripts/start_emulator.sh

The emulator runs on:

  • Firestore: localhost:8080
  • Emulator UI: localhost:4000

Running Tests

# Integration tests (requires Firebase emulator AND Android emulator running)
./scripts/run_tests.sh

Manual Development

# Start emulator (in one terminal)
./scripts/start_emulator.sh

# Run example app (in another terminal)
flutter run

📖 Usage

Auto-Generated Firestore IDs

// Create item with auto-generated ID
final item = repository.createWithAutoId(
  User(id: '', name: 'John Doe', createdAt: DateTime.now()),
  (user, id) => user.copyWith(id: id),
);

final savedUser = await repository.add(item);
print(savedUser.id); // Real Firestore document ID (20 characters)

Batch Operations

// Add multiple users
await userRepository.addAll([
  IdentifedObject('id1', user1),
  IdentifedObject('id2', user2),
]);

// Update multiple users
await userRepository.updateAll([
  IdentifedObject('id1', updatedUser1),
  IdentifedObject('id2', updatedUser2),
]);

// Delete multiple users
await userRepository.deleteAll(['id1', 'id2']);

Real-time Streaming

// Stream single document
userRepository.stream('user_id').listen((user) {
  print('User updated: ${user.name}');
});

// Stream query results
userRepository.streamQuery().listen((users) {
  print('Total users: ${users.length}');
});

Custom Queries

class UserQueryBuilder implements QueryBuilder<firestore.Query<Map<String, dynamic>>> {
  @override
  firestore.Query<Map<String, dynamic>> build(Query query) {
    final baseQuery = FirebaseFirestore.instance.collection('users');
    
    if (query is AgeQuery) {
      return baseQuery
          .where('age', isGreaterThan: query.minAge)
          .orderBy('age');
    }
    
    if (query is ActiveUsersQuery) {
      return baseQuery
          .where('status', isEqualTo: 'active')
          .orderBy('lastActive', descending: true);
    }
    
    return baseQuery.orderBy('createdAt', descending: true);
  }
}

// Use custom queries
final adults = await userRepository.query(query: AgeQuery(minAge: 18));
final activeUsers = userRepository.streamQuery(query: ActiveUsersQuery());

Error Handling

try {
  final user = await userRepository.get('non-existing-id');
} on RepositoryException catch (e) {
  if (e.code == RepositoryErrorCode.notFound) {
    print('User not found');
  } else if (e.code == RepositoryErrorCode.alreadyExists) {
    print('User already exists');
  }
}

🔄 Comparison with Other Implementations

For a detailed comparison of all repository implementations, see the main documentation.

📁 Example Application

See the example directory for a complete Flutter app demonstrating:

  • Real-time user management with Firestore
  • Auto-generated IDs and CRUD operations
  • Custom query system with search functionality
  • Modern Material 3 UI with error handling
  • Integration tests with Firebase emulator
cd example
./scripts/start_emulator.sh  # Terminal 1  
flutter run                  # Terminal 2

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.