DEV Community

Cover image for Local MongoDB in Flutter 😱🤯
somen das
somen das

Posted on

Local MongoDB in Flutter 😱🤯

FlutterDB

FlutterDB is a lightweight, SQLite-based document database for Flutter applications. It provides a MongoDB-like API for managing collections and documents while leveraging the performance benefits of SQLite.

Why do I build this?

So I am using MongoDB in my NodeJS application. Now I am building a chat application in Flutter, and for rendering performance, I thought I should store the messages locally and render a sliding window on it. Still, I have no luck finding a good local database that is flexible enough and also fast enough. And so I thought, why not build a MongoDB-like database in Flutter, which gets the job done. It's not a full-fledged MongoDB running locally, but it is similar to it, and most of the common tasks are going very well, as I tested.

Features

  • Document-oriented database with collections and documents
  • MongoDB-style query syntax and operations
  • Built on top of SQLite for performance and reliability
  • Unique ID generation for documents
  • Support for complex queries, updates, and aggregations
  • Batch operations for better performance

Installation

Add the following dependencies to your pubspec.yaml:

dependencies:
  flutterdb: ^0.0.1
Enter fullscreen mode Exit fullscreen mode

Getting Started

Import the package:

import 'package:flutterdb/flutterdb.dart';
Enter fullscreen mode Exit fullscreen mode

Initialize the database:

final db = FlutterDB();
Enter fullscreen mode Exit fullscreen mode

Basic Usage

Working with Collections

// Create or get a collection
final users = await db.collection('users');

// List all collections
final collections = await db.listCollections();

// Drop a collection
await db.dropCollection('users');
Enter fullscreen mode Exit fullscreen mode

CRUD Operations

Create

// Insert a single document
final userId = await users.insert({
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 30
});

// Insert multiple documents
final ids = await users.insertMany([
  {
    'name': 'Jane Smith',
    'email': '[email protected]',
    'age': 25
  },
  {
    'name': 'Bob Johnson',
    'email': '[email protected]',
    'age': 40
  }
]);
Enter fullscreen mode Exit fullscreen mode

Read

// Find all documents in a collection
final allUsers = await users.find({});

// Find documents with a simple query
final adults = await users.find({'age': {'$gte': 18}});

// Find a document by ID
final user = await users.findById(userId);

// Count documents
final userCount = await users.count({'age': {'$gt': 30}});
Enter fullscreen mode Exit fullscreen mode

Update

// Update a document by ID
await users.updateById(userId, {'status': 'active'});

// Update multiple documents
final updatedCount = await users.updateMany(
  {'age': {'$lt': 30}},
  {'status': 'young'}
);
Enter fullscreen mode Exit fullscreen mode

Delete

// Delete a document by ID
await users.deleteById(userId);

// Delete multiple documents
final deletedCount = await users.deleteMany({'status': 'inactive'});
Enter fullscreen mode Exit fullscreen mode

Advanced Queries

Comparison Operators

// Greater than
await users.find({'age': {'$gt': 30}});

// Less than or equal
await users.find({'age': {'$lte': 25}});

// Not equal
await users.find({'status': {'$ne': 'inactive'}});

// In array
await users.find({'role': {'$in': ['admin', 'moderator']}});
Enter fullscreen mode Exit fullscreen mode

Logical Operators

// AND
await users.find({
  '$and': [
    {'age': {'$gte': 18}},
    {'status': 'active'}
  ]
});

// OR
await users.find({
  '$or': [
    {'role': 'admin'},
    {'permissions': {'$in': ['write', 'delete']}}
  ]
});

// NOR
await users.find({
  '$nor': [
    {'status': 'banned'},
    {'role': 'guest'}
  ]
});
Enter fullscreen mode Exit fullscreen mode

Text Search

// Regex search
await users.find({'name': {'$regex': '^Jo'}});

// Simple text search
await users.find({'bio': {'$like': 'flutter developer'}});
Enter fullscreen mode Exit fullscreen mode

Aggregation

final results = await users.aggregate([
  {'$match': {'status': 'active'}},
  {'$sort': {'age': -1}},
  {'$skip': 10},
  {'$limit': 20},
  {'$project': {
          'name': 1,
          'age': 1,
          'city': 1,
          '_id': 0,
               }
  },
  {'$group': {
          '_id': '\$city',
          'count': {'\$sum': 1}
        }
  },
]);
Enter fullscreen mode Exit fullscreen mode

Best Practices

Performance Optimization

  1. Use batch operations for multiple insertions or updates:
   await users.insertMany(manyUsers); // Better than inserting one by one
Enter fullscreen mode Exit fullscreen mode
  1. Keep document size reasonable:

    • Large documents can slow down performance
    • Consider storing large binary data (images, files) separately
  2. Create indexes for frequently queried fields:

    • Currently, FlutterDB automatically indexes collection names
    • Future versions may support custom indexes
  3. Use appropriate queries:

    • Finding by ID is faster than complex queries
    • Limit results when possible using $limit

Data Structure

  1. Use consistent schemas:

    • While FlutterDB is schemaless, consistent document structures improve code maintainability
  2. Choose good document IDs:

    • Let FlutterDB generate IDs unless you have specific requirements
    • Custom IDs should be unique and consistent
  3. Handle relationships thoughtfully:

    • For one-to-many relationships, consider embedding related data or using references

Error Handling

Always implement error handling:

try {
  await users.insert({'name': 'John'});
} catch (e) {
  print('Error inserting document: $e');
  // Handle error appropriately
}
Enter fullscreen mode Exit fullscreen mode

Limitations

  1. Not a full MongoDB replacement:

    • Limited subset of MongoDB query operators
    • Some complex aggregation operations not supported
  2. Performance with large datasets:

    • Best suited for mobile apps with moderate data size
    • Consider pagination or limiting queries for large collections
  3. No built-in encryption:

    • Data is stored in plain text
    • Consider additional encryption for sensitive data
  4. Limited indexing options:

    • Custom indexes not yet supported
    • Consider query performance for large collections
  5. No network synchronization:

    • Local database only
    • Implement your own sync solution if needed

Complete Example

import 'package:flutter/material.dart';
import 'package:flutterdb/flutterdb.dart';

class UserRepository {
  final FlutterDB _db = FlutterDB();
  late Future<Collection> _users;

  UserRepository() {
    _users = _db.collection('users');
  }

  Future<String> addUser(String name, String email, int age) async {
    final collection = await _users;
    return await collection.insert({
      'name': name,
      'email': email,
      'age': age,
      'createdAt': DateTime.now().toIso8601String(),
    });
  }

  Future<List<Map<String, dynamic>>> getAdultUsers() async {
    final collection = await _users;
    return await collection.find({
      'age': {'$gte': 18},
    });
  }

  Future<void> updateUserStatus(String id, String status) async {
    final collection = await _users;
    await collection.updateById(id, {
      'status': status,
      'updatedAt': DateTime.now().toIso8601String(),
    });
  }
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final userRepo = UserRepository();

  // Add a user
  final userId = await userRepo.addUser('Alice', '[email protected]', 28);
  print('Added user with ID: $userId');

  // Update user status
  await userRepo.updateUserStatus(userId, 'premium');

  // Get all adult users
  final adults = await userRepo.getAdultUsers();
  for (var user in adults) {
    print('User: ${user['name']}, Age: ${user['age']}, Status: ${user['status']}');
  }
}
Enter fullscreen mode Exit fullscreen mode

Future Enhancements

  • Custom indexing support
  • More aggregation pipeline operators
  • Full-text search capabilities
  • Data encryption options
  • Schema validation
  • Observable queries
  • Migration support

Contributing

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

License

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

If you are searching for this keywords
"offline-first Flutter apps"
"local storage in Flutter"
"best local database Flutter"

your search ends here
I'd love your thoughts. Try FlutterDB in your next project and let me know how it goes! You can ⭐️ the GitHub repo or open an issue if you hit any snags. 🙌

Top comments (3)

Collapse
 
madhav_majumdar profile image
Madhav Majumdar

no need to specify database ?? just start with collections ??

Collapse
 
ktbsomen profile image
somen das • Edited

thank you for the comment.
yes you don't need db as it create a own db when you call a collection it just add a row to a sqflite table called collection and when adding a document to it it creates a foreign key on the collection name and just adds to a another table called documents

it's very simple you can check the codes its very easy to understand and some part of it is just random placeholders in the query builders like geo queries i didn't implement

i created it this way because i used to use Atlas which directly build a db from url so that practice is reflected here sorry for inconvenience you can contribute though

Collapse
 
madhav_majumdar profile image
Madhav Majumdar

That's pretty cool