UNPKG

react-native-sst-storage-db

Version:

A powerful file-based MongoDB-like database for React Native. Single JSON file storage with collections, advanced querying, indexing, and atomic operations. No AsyncStorage dependency.

612 lines (465 loc) โ€ข 16.5 kB
# ๐Ÿ—„๏ธ react-native-sst-storage-db A powerful **file-based** MongoDB-like database for React Native. Stores all data in a single JSON file with collections, advanced querying, indexing, and atomic operations. **No AsyncStorage dependency.** [![npm version](https://badge.fury.io/js/react-native-sst-storage-db.svg)](https://badge.fury.io/js/react-native-sst-storage-db) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ## โœจ Features - **๐Ÿ—„๏ธ File-Based Database** - Single JSON file acts as your database, no external dependencies - **๐Ÿ“„ MongoDB-like API** - Familiar database operations with collections and documents - **๐Ÿ” Advanced Queries** - Support for operators like `$lt`, `$gt`, `$in`, `$regex`, `$and`, `$or`, etc. - **๐Ÿ’พ Atomic File Operations** - Safe writes with atomic file operations and backup system - **๐Ÿ“Š Indexing Support** - Create indexes on fields for improved query performance - **๐Ÿ”— TypeScript Support** - Full type definitions included - **๐Ÿ—‚๏ธ Key-Value Storage** - Simple key-value operations alongside collections - **๐Ÿ”„ Backup & Restore** - Export and import functionality for data migration - **โšก Performance Optimized** - Efficient in-memory operations with file persistence - **๐Ÿ“ฑ React Native Ready** - Built specifically for React Native with react-native-fs ## ๐Ÿ“ฆ Installation ```bash npm install react-native-sst-storage-db react-native-fs ``` ### iOS Setup ```bash cd ios && pod install ``` ### Android Setup No additional setup required for Android. **Note:** This library uses `react-native-fs` for file operations, not AsyncStorage. Your data is stored in a single JSON file on the device's document directory. ## ๐Ÿ”„ Changelog ### v2.0.3 (Latest) - **๐Ÿ› Android Persistence Fix**: Fixed data loss issue on Android app restart - **๐Ÿ“ Smart Directory Selection**: Automatically selects most persistent directory (ExternalDirectoryPath > DocumentDirectoryPath) - **๐Ÿ” Directory Verification**: Added writability verification with automatic fallback - **๐Ÿ“ฑ Cross-Platform Persistence**: Ensures data persists across app restarts on both iOS and Android ### v2.0.2 - **๐Ÿ› iOS Fix**: Resolved "file already exists" errors during atomic file operations - **๐Ÿ”ง Enhanced Retry Logic**: Improved file system operations with better error handling - **๐Ÿ›ก๏ธ Automatic Recovery**: Added fallback mechanisms for failed initialization - **๐Ÿ“ฑ Better iOS Compatibility**: Optimized for iOS file system behavior ### v2.0.1 - Initial release with file-based architecture ## ๐Ÿ—๏ธ Architecture Unlike traditional React Native storage solutions, SST Storage uses a **single JSON file** as your database: ``` ๐Ÿ“ Your App โ”œโ”€โ”€ ๐Ÿ“„ SSTStorage.json โ† Your entire database โ”‚ โ”œโ”€โ”€ ๐Ÿ“Š metadata โ”‚ โ”œโ”€โ”€ ๐Ÿ“š collections โ”‚ โ”‚ โ”œโ”€โ”€ users: [doc1, doc2...] โ”‚ โ”‚ โ”œโ”€โ”€ products: [doc1, doc2...] โ”‚ โ”‚ โ””โ”€โ”€ orders: [doc1, doc2...] โ”‚ โ”œโ”€โ”€ ๐Ÿ” indexes โ”‚ โ””โ”€โ”€ ๐Ÿ—‚๏ธ keyValueStore ``` **Benefits:** - โœ… **Simple backup** - Just one file to backup/restore - โœ… **Atomic operations** - All changes are atomic - โœ… **No dependencies** - No need for AsyncStorage or SQLite - โœ… **Cross-platform** - Works identically on iOS and Android - โœ… **Debuggable** - You can inspect the JSON file directly ## ๐Ÿš€ Quick Start ### Basic Usage ```javascript import { SSTStorage } from 'react-native-sst-storage-db'; // Initialize storage const storage = new SSTStorage(); await storage.initialize(); // Key-value operations await storage.set('user_settings', { theme: 'dark', language: 'en' }); const settings = await storage.get('user_settings'); // Collection operations const users = storage.collection('users'); // Insert documents const user = await users.insert({ name: 'John Doe', email: 'john@example.com', age: 30 }); // Query documents const allUsers = await users.find(); const youngUsers = await users.find({ age: { $lt: 25 } }); // Update documents await users.updateById(user._id, { age: 31 }); // Delete documents await users.deleteById(user._id); ``` ### TypeScript Usage ```typescript import { SSTStorage, Document } from 'react-native-sst-storage-db'; interface User extends Document { name: string; email: string; age: number; } const storage = new SSTStorage(); await storage.initialize(); const users = storage.collection('users'); const user: User = await users.insert({ name: 'John Doe', email: 'john@example.com', age: 30 }); ``` ## ๐Ÿ“– API Documentation ### SSTStorage Class #### Constructor ```javascript const storage = new SSTStorage(options); ``` **Options:** - `databaseName` (string): Database filename (default: 'SSTStorage') - `databasePath` (string): Custom database file path (optional) - `autoSave` (boolean): Auto-save to file (default: true) - `enableIndexing` (boolean): Enable indexing (default: true) - `enableLogging` (boolean): Enable console logging (default: false) - `maxCollections` (number): Maximum number of collections (default: 100) - `maxDocuments` (number): Maximum documents per collection (default: 50000) - `backupCount` (number): Number of backup files to keep (default: 3) #### Methods ##### `initialize(): Promise<boolean>` Initialize the storage system and load existing data. ```javascript await storage.initialize(); ``` ##### `set(key: string, value: any): Promise<boolean>` Store a key-value pair. ```javascript await storage.set('config', { version: '1.0' }); ``` ##### `get(key: string): Promise<any>` Retrieve a value by key. ```javascript const config = await storage.get('config'); ``` ##### `collection(name: string): Collection` Get or create a collection. ```javascript const users = storage.collection('users'); ``` ##### `getInfo(): Promise<StorageInfo>` Get storage statistics and information. ```javascript const info = await storage.getInfo(); // Returns: { initialized, collections, keyValuePairs, options } ``` ### Collection Class #### CRUD Operations ##### `insert(document: object): Promise<Document>` Insert a single document. ```javascript const user = await users.insert({ name: 'Alice', email: 'alice@example.com' }); // Returns: { _id, name, email, _createdAt, _updatedAt } ``` ##### `insertMany(documents: object[]): Promise<Document[]>` Insert multiple documents. ```javascript const result = await users.insertMany([ { name: 'Bob', age: 25 }, { name: 'Carol', age: 30 } ]); ``` ##### `find(query?: object, options?: QueryOptions): Promise<Document[]>` Find documents matching a query. ```javascript // Find all documents const allUsers = await users.find(); // Find with query const adults = await users.find({ age: { $gte: 18 } }); // Find with options const youngUsers = await users.find( { age: { $lt: 30 } }, { sort: { name: 1 }, limit: 10 } ); ``` ##### `findOne(query?: object): Promise<Document | null>` Find a single document. ```javascript const user = await users.findOne({ email: 'john@example.com' }); ``` ##### `updateById(id: string, updateData: object): Promise<Document | null>` Update a document by ID. ```javascript const updated = await users.updateById(userId, { age: 31, lastLogin: new Date().toISOString() }); ``` ##### `deleteById(id: string): Promise<Document | null>` Delete a document by ID. ```javascript const deleted = await users.deleteById(userId); ``` #### Advanced Queries ##### Query Operators ```javascript // Comparison operators await users.find({ age: { $gt: 18 } }); // Greater than await users.find({ age: { $gte: 18 } }); // Greater than or equal await users.find({ age: { $lt: 65 } }); // Less than await users.find({ age: { $lte: 65 } }); // Less than or equal await users.find({ age: { $ne: 30 } }); // Not equal // Array operators await users.find({ role: { $in: ['admin', 'moderator'] } }); await users.find({ status: { $nin: ['banned', 'suspended'] } }); // Existence and type operators await users.find({ email: { $exists: true } }); // Regular expression await users.find({ name: { $regex: '^John' } }); // Array size await users.find({ hobbies: { $size: 3 } }); ``` ##### Update Operators ```javascript // Set fields await users.updateById(id, { $set: { status: 'active', lastLogin: new Date() } }); // Increment numeric fields await users.updateById(id, { $inc: { loginCount: 1, points: 100 } }); // Add to arrays await users.updateById(id, { $push: { hobbies: 'reading' } }); // Remove from arrays await users.updateById(id, { $pull: { tags: 'deprecated' } }); // Remove fields await users.updateById(id, { $unset: { tempField: 1 } }); ``` ## ๐ŸŽฏ Real-World Examples ### Grocery List App ```javascript const storage = new SSTStorage(); await storage.initialize(); const groceries = storage.collection('groceries'); // Add items await groceries.insertMany([ { name: 'Milk', category: 'Dairy', price: 2.99, purchased: false }, { name: 'Bread', category: 'Bakery', price: 1.50, purchased: false }, { name: 'Apples', category: 'Fruits', price: 3.99, purchased: false } ]); // Mark item as purchased await groceries.updateOne( { name: 'Milk' }, { $set: { purchased: true, purchasedAt: new Date() } } ); // Get unpurchased items const pending = await groceries.find({ purchased: false }); // Get items by category const dairy = await groceries.find({ category: 'Dairy' }); // Calculate total cost const allItems = await groceries.find(); const total = allItems.reduce((sum, item) => sum + item.price, 0); ``` ### User Management System ```javascript const users = storage.collection('users'); // Create user profiles await users.insert({ username: 'john_doe', email: 'john@example.com', profile: { firstName: 'John', lastName: 'Doe', avatar: 'https://example.com/avatar.jpg' }, preferences: { theme: 'dark', notifications: true }, createdAt: new Date().toISOString() }); // Find users by nested fields const darkThemeUsers = await users.find({ 'preferences.theme': 'dark' }); // Update user preferences await users.updateById(userId, { $set: { 'preferences.language': 'es', 'profile.lastSeen': new Date().toISOString() } }); ``` ### Chat Messages Storage ```javascript const messages = storage.collection('messages'); // Store messages await messages.insert({ chatId: 'chat_123', senderId: 'user_456', message: 'Hello there!', timestamp: Date.now(), read: false }); // Get recent messages for a chat const recentMessages = await messages.find( { chatId: 'chat_123' }, { sort: { timestamp: -1 }, limit: 50 } ); // Mark messages as read await messages.updateMany( { chatId: 'chat_123', read: false }, { $set: { read: true } } ); // Get unread message count const unreadCount = await messages.count({ read: false }); ``` ## ๐Ÿ”ง Advanced Usage ### Configuration Options ```javascript const storage = new SSTStorage({ databaseName: 'MyAppDB', // Custom database filename enableLogging: true, // Enable detailed logging enableIndexing: true, // Enable indexing for performance maxCollections: 100, // Increase collection limit maxDocuments: 50000, // Increase document limit per collection backupCount: 5 // Keep 5 backup files }); ``` ### Backup and Restore **Simple File-Based Backup:** ```javascript // Method 1: Export to JavaScript object (for cloud backup) const backupData = await storage.exportData(); // Send backupData to your cloud storage, server, etc. // Method 2: Direct file access (for local backup) const databasePath = storage.databasePath; // Copy the file directly: /path/to/SSTStorage.json // Restore from backup await storage.importData(backupData); ``` **Automatic Backups:** The library automatically creates backup files when corruption is detected: ``` ๐Ÿ“ App Documents โ”œโ”€โ”€ SSTStorage.json โ† Main database โ”œโ”€โ”€ SSTStorage.json.corrupted.123 โ† Auto backup โ””โ”€โ”€ SSTStorage.json.pre-restore.456 โ† Pre-restore backup ``` ### Performance Tips 1. **Use indexes for frequently queried fields** (coming in future versions) 2. **Limit query results** with `limit` option for large datasets 3. **Use projection** to fetch only needed fields 4. **Batch operations** with `insertMany` instead of multiple `insert` calls ### Error Handling ```javascript try { const users = storage.collection('users'); await users.insert({ name: 'John' }); } catch (error) { console.error('Storage operation failed:', error.message); if (error.message.includes('not initialized')) { // Handle initialization error await storage.initialize(); } } ``` ## ๐Ÿงช Testing ```javascript // Test file example import { SSTStorage } from 'react-native-sst-storage-db'; describe('SSTStorage', () => { let storage; beforeEach(async () => { storage = new SSTStorage({ prefix: 'test_' }); await storage.initialize(); }); afterEach(async () => { await storage.clear(); }); test('should insert and find documents', async () => { const users = storage.collection('users'); const user = await users.insert({ name: 'Test User' }); expect(user._id).toBeDefined(); expect(user.name).toBe('Test User'); const found = await users.findById(user._id); expect(found.name).toBe('Test User'); }); }); ``` ## ๐Ÿ“ฑ React Native Integration ### With React Hooks ```javascript import React, { useState, useEffect } from 'react'; import { SSTStorage } from 'react-native-sst-storage-db'; const useStorage = () => { const [storage, setStorage] = useState(null); useEffect(() => { const initStorage = async () => { const storageInstance = new SSTStorage(); await storageInstance.initialize(); setStorage(storageInstance); }; initStorage(); }, []); return storage; }; // In your component const MyComponent = () => { const storage = useStorage(); const [users, setUsers] = useState([]); useEffect(() => { if (storage) { loadUsers(); } }, [storage]); const loadUsers = async () => { const usersCollection = storage.collection('users'); const userList = await usersCollection.find(); setUsers(userList); }; const addUser = async (userData) => { const usersCollection = storage.collection('users'); await usersCollection.insert(userData); await loadUsers(); // Refresh list }; // ... rest of component }; ``` ### With Redux ```javascript // actions.js import { SSTStorage } from 'react-native-sst-storage-db'; const storage = new SSTStorage(); export const initStorage = () => async (dispatch) => { await storage.initialize(); dispatch({ type: 'STORAGE_INITIALIZED' }); }; export const addUser = (userData) => async (dispatch) => { const users = storage.collection('users'); const newUser = await users.insert(userData); dispatch({ type: 'USER_ADDED', payload: newUser }); }; ``` ## ๐Ÿค Contributing Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details. ### Development Setup 1. Clone the repository 2. Install dependencies: `npm install` 3. Run tests: `npm test` 4. Build: `npm run build` ## ๐Ÿ“„ License MIT License. See [LICENSE](LICENSE) file for details. ## ๐Ÿ†˜ Support - ๐Ÿ“– [Documentation](https://github.com/yourusername/react-native-sst-storage-db/wiki) - ๐Ÿ› [Issue Tracker](https://github.com/yourusername/react-native-sst-storage-db/issues) - ๐Ÿ’ฌ [Discussions](https://github.com/yourusername/react-native-sst-storage-db/discussions) ## ๐Ÿ™ Acknowledgments - Built for the React Native community - Inspired by MongoDB's query syntax - Uses AsyncStorage for data persistence --- Made with โค๏ธ for React Native developers