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
Markdown
# ๐๏ธ 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.**
[](https://badge.fury.io/js/react-native-sst-storage-db)
[](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