A TypeScript CLI tool that automatically generates Dart models from Firestore collections by analyzing document schemas in your Firebase project.
- 🎮 Interactive Mode: Guided CLI experience with collection selection
- 🔥 Connects directly to Firebase Firestore
- 📊 Analyzes real documents to infer field types
- 🎯 Generates clean Dart models with
fromJson/toJson - 🔍 Detects optional and nullable fields automatically
- ✅ Multiple selection: Choose multiple collections at once with checkboxes
- 🌳 Auto-discovery: Automatically detects and offers subcollections
- 🎨 Auto-formats generated code with
dart format - ⚡ Fast and easy to use
npm install -g firestore-dart-generatornpx firestore-dart-generator --helpnpm install --save-dev firestore-dart-generator-
Install the tool:
npm install -g firestore-dart-generator
-
Run interactively:
firestore-dart-gen --service-account firebase_service_account.json
-
Follow the prompts:
- ✨ View your Firebase project name
- ✅ Select collections with checkboxes (Space to select, Enter to confirm)
- 🌳 Choose whether to include subcollections (auto-detected)
- 📁 Configure output directory and sample size
- 🎯 Review summary and confirm
Done! Your Dart models are generated.
- Node.js 18+ with npm
- Firebase project with Firestore
- Firebase service account JSON file (for authentication)
- (Optional) Dart SDK for auto-formatting
- Go to Firebase Console
- Select your project
- Go to Project Settings > Service Accounts
- Click Generate New Private Key
- Save the JSON file securely (e.g.,
firebase_service_account.json)
Create a .env file in your project directory:
GOOGLE_APPLICATION_CREDENTIALS=./firebase_service_account.json
FIREBASE_PROJECT_ID=your-project-idOr use the --service-account and --project-id flags when running the command.
You can create a firestore-dart-gen.yaml file to:
- ✅ Specify Firebase credentials (no need to type them every time)
- ✅ Pre-select collections you use frequently
- ✅ Set default output directory and sample size
- ✅ Share configuration with your team (without credentials)
Create firestore-dart-gen.yaml in your project root:
# Firebase Configuration
firebase:
serviceAccount: ./firebase_service_account.json
projectId: my-project-id # optional
# Pre-select these collections in the CLI
collections:
- users
- products
- orders
# Default output settings
output:
directory: ./lib/src/models
sampleSize: 20
subcollectionSearchLimit: 50 # Max parent docs to search for subcollectionsCopy from the example file:
cp firestore-dart-gen.example.yaml firestore-dart-gen.yaml
# Edit with your settings# CLI will automatically find firestore-dart-gen.yaml
firestore-dart-gen
# Or specify a custom config file
firestore-dart-gen --config my-config.yaml
# Override config with CLI arguments
firestore-dart-gen --service-account other.jsonConfiguration is resolved in this order (highest to lowest priority):
- CLI arguments (e.g.,
--service-account other.json) - Config file (
firestore-dart-gen.yaml) - Environment variables (
.envfile)
Example: If you have serviceAccount in the YAML but also pass --service-account, the CLI argument wins.
- ✅ Save time - no need to type credentials every run
- ✅ Pre-select collections you work with frequently
- ✅ Team consistency - share same defaults
- ✅ Flexible - can override with CLI args anytime
firestore-dart-gen.yaml to .gitignore if it contains sensitive paths!
The tool runs in interactive mode by default, providing a guided experience:
Features:
- 🔍 Auto-discovery: Automatically lists all collections in your project
- ✅ Multiple selection: Use checkboxes to select multiple collections at once
- 🌳 Subcollection detection: Automatically finds and offers to include subcollections
- ⚙️ Easy configuration: Configure output directory and sample size with prompts
- 📊 Summary review: See exactly what will be generated before confirming
Run the interactive mode:
firestore-dart-gen --service-account firebase_service_account.jsonExample Session:
🔥 Firestore Dart Generator - Interactive Mode
✓ Connected to Firebase Project: my-awesome-app
🔍 Discovering collections...
Found 5 collection(s)
? Select collections to generate models for: (Use arrow keys, Space to select, Enter to confirm)
❯◯ users
◯ products
◯ orders
◯ reviews
◯ settings
🌳 Checking for subcollections...
Analyzing users...
Found 2 subcollection(s): profiles, settings
Include subcollections for users? (Y/n) Yes
? Output directory for generated Dart files: ./lib/src/models
? Number of documents to sample per collection: 20
? Maximum parent documents to search for subcollections: 50
📋 Generation Summary:
────────────────────────────────────────────────────────────
Firebase Project: my-awesome-app
Collections: users, products
Subcollections:
└─ users: profiles, settings
Output: ./lib/src/models
Sample Size: 20 documents per collection
Subcollection Search Limit: 50 parent documents
────────────────────────────────────────────────────────────
? Generate Dart models with these settings? (Y/n) Yes
🚀 Starting generation...
📦 Processing collection: users
...
✨ Success! Generated 4 model(s)
Generated files:
✓ lib/src/models/user_dto.dart
✓ lib/src/models/profile_dto.dart
✓ lib/src/models/setting_dto.dart
✓ lib/src/models/product_dto.dart
📚 Next steps:
1. Review the generated files
2. Import the models in your Dart code
3. Add 'equatable' to your pubspec.yaml if not already present
Note: Interactive mode is now recommended for most use cases.
Extract multiple collections using a YAML configuration file:
- Create
collections.yaml(or copy fromcollections.example.yaml):
collections:
- name: users
output: ./lib/src/models
sampleSize: 20
subcollections:
- profiles
- settings
- name: products
output: ./lib/src/models- Run the extractor:
firestore-dart-gen batch --service-account firebase_service_account.jsonThat's it! All collections in the YAML file will be processed.
Note: Interactive mode is now recommended for most use cases.
For programmatic use or CI/CD, you can still extract collections using the old commands (requires custom wrapper):
firestore-dart-gen single \
--collection users \
--output ./lib/src/models \
--service-account firebase_service_account.jsonfirestore-dart-gen single \
--collection orders \
--subcollections items,shipping_info \
--output ./lib/src/models \
--service-account firebase_service_account.json| Option | Required | Description | Default |
|---|---|---|---|
--service-account <path> |
No | Path to service account JSON | From env |
--project-id <id> |
No | Firebase project ID | From env |
| Option | Alias | Required | Description | Default |
|---|---|---|---|---|
--config <path> |
-f |
No | Path to collections.yaml | collections.yaml |
--service-account <path> |
- | No | Path to service account JSON | From env |
--project-id <id> |
- | No | Firebase project ID | From env |
| Option | Alias | Required | Description | Default |
|---|---|---|---|---|
--collection <name> |
-c |
Yes | Firestore collection name | - |
--output <path> |
-o |
Yes | Output directory for Dart files | - |
--subcollections <names> |
-s |
No | Comma-separated subcollections | - |
--service-account <path> |
- | No | Path to service account JSON | From env |
--project-id <id> |
- | No | Firebase project ID | From env |
--sample-size <number> |
- | No | Number of docs to sample | 20 |
The collections.yaml file defines which collections to extract:
collections:
# Simple collection
- name: users
output: ./lib/src/models
sampleSize: 20 # Optional: number of documents to sample (default: 20)
# Collection with subcollections
- name: orders
output: ./lib/src/models
subcollections:
- items # Will extract from orders/{orderId}/items
- shipping_info # Will extract from orders/{orderId}/shipping_info{
"id": "user123",
"email": "user@example.com",
"name": "John Doe",
"age": 30,
"isActive": true,
"createdAt": Timestamp(2024-01-01),
"metadata": {
"source": "mobile"
}
}import 'package:equatable/equatable.dart';
/// {@template user}
/// A model representing a users document from Firestore.
/// {@endtemplate}
class User extends Equatable {
/// {@macro user}
const User({
required this.id,
required this.email,
required this.isActive,
this.age,
this.name,
this.createdAt,
this.metadata,
});
/// Creates a [User] from a JSON map.
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
email: json['email'] as String,
isActive: json['isActive'] as bool,
age: json['age'] as int?,
name: json['name'] as String?,
createdAt: json['createdAt'] != null
? DateTime.parse(json['createdAt'] as String)
: null,
metadata: json['metadata'] as Map<String, dynamic>?,
);
}
/// The id field.
final String id;
/// The email field.
final String email;
/// The isActive field.
final bool isActive;
/// The age field.
final int? age;
/// The name field.
final String? name;
/// The createdAt field.
final DateTime? createdAt;
/// The metadata field.
final Map<String, dynamic>? metadata;
@override
List<Object?> get props => [
id,
email,
isActive,
age,
name,
createdAt,
metadata,
];
/// Converts this [User] to a JSON map.
Map<String, dynamic> toJson() {
return {
'id': id,
'email': email,
'isActive': isActive,
if (age != null) 'age': age,
if (name != null) 'name': name,
if (createdAt != null) 'createdAt': createdAt?.toIso8601String(),
if (metadata != null) 'metadata': metadata,
};
}
}The tool automatically detects and maps Firestore types to Dart types:
| Firestore Type | Dart Type |
|---|---|
| String | String |
| Number (integer) | int |
| Number (float) | double |
| Boolean | bool |
| Timestamp | DateTime |
| Array | List<dynamic> |
| Map | Map<String, dynamic> |
| Null/Mixed | dynamic |
- A field is marked optional (
field?) if it doesn't appear in all sampled documents - Optional fields use
if (field != null)intoJson()
- A field is marked nullable (
Type?) if it has inconsistent types across documents - Required fields present in all documents are non-nullable
firestore-dart-gen single \
--collection users \
--output ./lib/src/models \
--service-account firebase_service_account.jsonNote: The tool automatically runs dart format on generated files, so formatting is already done!
The generated models are basic. You may want to:
- Add more detailed documentation
- Customize field names (if Firestore uses different naming)
- Add validation logic
- Use
json_serializablefor more complex scenarios
import 'package:your_package/models/user.dart';
final user = User.fromJson(firestoreDoc.data()!);Make sure the path to your service account JSON is correct. Use absolute paths or paths relative to your current directory.
- Verify the collection name is correct
- Ensure the collection has at least one document
- Check Firebase rules allow read access for the service account
This happens when a field has different types across documents. The tool will use dynamic type. Consider:
- Cleaning up inconsistent data in Firestore
- Manually editing the generated model to use the correct type
The tool tries to auto-format generated files with dart format. If Dart SDK is not in your PATH, you'll see a warning:
⚠ Could not format files: dart command not found
This is non-fatal - your files are still generated correctly, just not formatted. To fix:
- Install Dart SDK (if not installed): https://dart.dev/get-dart
- Add to PATH: Make sure
dartcommand is available in your terminal - Manual format: Run
dart format <output-directory>manually after generation
Or ignore it - the generated code is valid, just might need manual formatting.
- Clone the repository
- Install dependencies:
npm install - Build:
npm run build - Run:
npm run extractornpm run extract:single
The project includes comprehensive golden tests to ensure code generation quality:
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage report
npm run test:coverage
# Update golden files (when intentionally changing output)
npm run test:update-goldensGolden tests verify that the generated Dart code matches expected output exactly. See test/README.md for more details.
firestore-dart-generator/
├── src/
│ ├── index.ts # CLI entry point
│ ├── firestore-client.ts # Firebase connection
│ ├── schema-analyzer.ts # Type detection logic
│ ├── dart-generator.ts # Code generation
│ ├── config-loader.ts # YAML config loader
│ ├── types.ts # TypeScript interfaces
│ └── templates/
│ └── model.hbs # Handlebars template
├── dist/ # Compiled JavaScript
├── package.json
├── tsconfig.json
└── README.md
⚠️ Never commit service account JSON files- Add
*service-account.jsonto.gitignore - Use environment variables for sensitive data
- Limit service account permissions to read-only Firestore access
Contributions are welcome! Please feel free to submit a Pull Request.
MIT
If you encounter any issues or have questions, please open an issue on GitHub.