mongo_document_annotation 1.2.9 copy "mongo_document_annotation: ^1.2.9" to clipboard
mongo_document_annotation: ^1.2.9 copied to clipboard

Generate MongoDB CRUD code in Dart using @MongoDocument annotations - ideal for clean, scalable backend models.

pub package license

Table of Contents #

  1. Overview

  2. Features

  3. Getting Started

  4. Usage

  5. Configuration & Conventions

  6. Troubleshooting

  7. Contributing

  8. License

Overview #

mongo_document bridges Dart freezed models and MongoDB via mongo_dart, generating zero‑boilerplate, type‑safe CRUD and query builders that respect your Dart-native naming conventions (e.g. camelCase) while serializing to and from your DB schema (e.g. snake_case).

⚠️ Work in Progress: Experimental features may change. Your feedback and contributions are welcome.

Motivation #

When your Dart models use camelCase, but your database schema uses a different naming style (e.g. snake_case or any other convention), manual mapping between the two becomes tedious and error-prone. mongo_document removes that friction—letting you CRUD and query directly from your Dart model definitions, regardless of how you choose to name fields in MongoDB.

Features #

  • Zero‑Boilerplate CRUD & Queries: .save(), .delete(), .findOne(), .findMany(), .findOneByNamed(), .findManyByNamed(), .findById()
  • Batch Operations: .saveMany(List<T> documents) for bulk inserts; .updateOne(predicate, namedArgumentsOfUpdates) for targeted updates
  • Type-Safe DSL & Named Filters: Lambda-based predicates (p => p.field.eq(...)) or named-argument filters matching your model
  • Automatic Field Mapping: Honors @JsonSerializable(fieldRename)—camelCase in Dart, snake_case in MongoDB—and respects explicit @JsonKey(name) overrides
  • Nested References & Projections: Generates *Projections helper classes for each nested @MongoDocument type
  • Joins, Arrays & Maps: Built-in $lookup for references; QList and QMap support array/map operations
  • Timestamps & IDs: Auto-manage _id, created_at, and updated_at

Getting Started #

Prerequisites #

  • Dart SDK ≥ 3.0
  • A running MongoDB instance (local or remote)
  • MongoDB server version ≥ 3.6

Installation #

Add to pubspec.yaml:

dependencies:
  freezed_annotation: ">=2.4.4 <4.0.0"
  json_annotation: ^4.9.0
  mongo_document_annotation: ^1.2.9

dev_dependencies:
  build_runner: ^2.4.14
  freezed: ">=2.5.8 <4.0.0"
  json_serializable: ^6.9.3
  mongo_document: ^1.2.9

Then:

dart pub get

Initialization #

In your application entrypoint (e.g. main()), configure the MongoDB connection once:

import 'package:mongo_document_annotation/mongo_document_annotation.dart';

Future<void> main() async {
  await MongoConnection.initialize('mongodb://localhost:27017/mydb');
  // Now you can use generated .save(), .findOne(), etc.

  // Handle graceful shutdown
  ProcessSignal.sigint.watch().listen((_) async {
    print('SIGINT received. Shutting down gracefully...');
    await MongoDbConnection.shutdown();
    exit(0);
  });

  ProcessSignal.sigterm.watch().listen((_) async {
    print('SIGTERM received. Shutting down gracefully...');
    await MongoDbConnection.shutdown();
    exit(0);
  });
}

Usage #

Defining Models #

⚠️ Requirement: Every @MongoDocument class must include an ObjectId field in its primary constructor annotated with @ObjectIdConverter() and @JsonKey(name: '_id'). This ensures a valid MongoDB _id is always present.

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:mongo_document_annotation/mongo_document_annotation.dart';
part 'post.freezed.dart';
part 'post.g.dart';
part 'post.mongo_document.dart';

@MongoDocument(collection: 'posts')
@freezed
abstract class Post with _$Post {
  @JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
  const factory Post({
    @ObjectIdConverter() @JsonKey(name: '_id') ObjectId? id,
    User? author,
    String? body,
    @JsonKey(name:'post_note') String? postNote,
    @Default(<String>[]) List<String> tags,
    @DateTimeConverter() DateTime? createdAt,
    @DateTimeConverter() DateTime? updatedAt,
  }) = _Post;

  factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
}

Generating Code #

Run build_runner:

dart run build_runner build --delete-conflicting-outputs

This generates:

  • Instance methods: .save(), .delete(),
  • Static APIs: Posts.saveMany(), Posts.findOne(), Posts.findMany(), Posts.findById(), Posts.findOneByNamed(), Posts.findManyByNamed(),Posts.updateOne(...)
  • Query builder QPost with typed fields

CRUD Examples #

// Create & Save
final post = await Post(body: 'Hello world', tags: ['intro']).save();

// Batch Save
await Posts.saveMany([
  Post(body: 'Batch A'),
  Post(body: 'Batch B')
]);

// Update via copyWith and finally save()
await post?.copyWith(body: 'Updated').save();

// Targeted updateOne
await Posts.updateOne(
  (p) => p.body.eq('Hello world'),
  body: 'Updated via updateOne'
);

Advanced Queries & Projections #

All query methods—including .findOne(), .findMany(), .findOneByNamed() and .findManyByNamed()—support an optional projections parameter. Projection helper classes are generated for each nested @MongoDocument type in your model (e.g. for a User? author field you get PostAuthorProjections). Use these with the corresponding *Fields enums to include or exclude fields.

// Named-argument single query with exclusions
final result = await Posts.findOneByNamed(
  body: 'Secret Post',
  projections: [
    PostAuthorProjections(exclusions: [PostAuthorFields.password])
  ]
);

// DSL query with inclusions
Post? postWithAuthorNames = await Posts.findOne(
  (p) => p.body.eq('Hello'),
  projections: [
    PostAuthorProjections(inclusions: [PostAuthorFields.firstName, PostAuthorFields.lastName])
  ]
);

Named-Argument Queries & Projections #

// Named-argument many query
List<Post> intros = await Posts.findManyByNamed(body: 'Welcome', tags: ['intro']);

// Targeted deleteManyByNamed
await Posts.deleteManyByNamed(body:'Hello World');

Nested-Document Queries & Projections #

When querying via a nested @MongoDocument, you must include projection helpers for both the root and the nested documents to ensure fields are returned. Otherwise, queries against nested fields will not return any data even if matching documents exist.

// Querying only nested field without projection - returns null
final postNull = await Posts.findOne((p) => p.author.id(authorId));

// Project all root Post fields
final postAllFields = await Posts.findOne(
  (p) => p.author.id(authorId),
  projections: [
    PostProjections()
  ]
);

// Project both Post and nested author fields
final postWithAuthor = await Posts.findOne(
  (p) => p.author.id(authorId),
  projections: [
    PostProjections(),
    PostAuthorProjections()
  ]
);

Both PostProjections and PostAuthorProjections also support inclusions and exclusions parameters to customize which fields to include or omit in the result.

Configuration & Conventions #

  • Converters: @ObjectIdConverter(), @DateTimeConverter()
  • Collection name from @MongoDocument(collection: ...)

Troubleshooting #

Add to analysis_options.yaml:

analyzer:
  errors:
    invalid_annotation_target: ignore

Contributing #

See CONTRIBUTING.md for guidelines.

License #

This project is licensed under the MIT License. See LICENSE for details.

1
likes
0
points
2.12k
downloads

Publisher

unverified uploader

Weekly Downloads

Generate MongoDB CRUD code in Dart using @MongoDocument annotations - ideal for clean, scalable backend models.

Repository (GitHub)
View/report issues

Topics

#mongodb #codegen #annotations #dart #backend

Documentation

Documentation

License

unknown (license)

Dependencies

freezed_annotation, json_annotation, meta, mongo_dart, path

More

Packages that depend on mongo_document_annotation