react-native-sst-storage-db
v2.0.3
Published
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.
Maintainers
Readme
🗄️ 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.
✨ 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
npm install react-native-sst-storage-db react-native-fsiOS Setup
cd ios && pod installAndroid 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
│ └── 🗂️ keyValueStoreBenefits:
- ✅ 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
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: '[email protected]',
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
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: '[email protected]',
age: 30
});📖 API Documentation
SSTStorage Class
Constructor
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.
await storage.initialize();set(key: string, value: any): Promise<boolean>
Store a key-value pair.
await storage.set('config', { version: '1.0' });get(key: string): Promise<any>
Retrieve a value by key.
const config = await storage.get('config');collection(name: string): Collection
Get or create a collection.
const users = storage.collection('users');getInfo(): Promise<StorageInfo>
Get storage statistics and information.
const info = await storage.getInfo();
// Returns: { initialized, collections, keyValuePairs, options }Collection Class
CRUD Operations
insert(document: object): Promise<Document>
Insert a single document.
const user = await users.insert({
name: 'Alice',
email: '[email protected]'
});
// Returns: { _id, name, email, _createdAt, _updatedAt }insertMany(documents: object[]): Promise<Document[]>
Insert multiple documents.
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.
// 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.
const user = await users.findOne({ email: '[email protected]' });updateById(id: string, updateData: object): Promise<Document | null>
Update a document by ID.
const updated = await users.updateById(userId, {
age: 31,
lastLogin: new Date().toISOString()
});deleteById(id: string): Promise<Document | null>
Delete a document by ID.
const deleted = await users.deleteById(userId);Advanced Queries
Query Operators
// 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
// 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
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
const users = storage.collection('users');
// Create user profiles
await users.insert({
username: 'john_doe',
email: '[email protected]',
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
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
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:
// 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 backupPerformance Tips
- Use indexes for frequently queried fields (coming in future versions)
- Limit query results with
limitoption for large datasets - Use projection to fetch only needed fields
- Batch operations with
insertManyinstead of multipleinsertcalls
Error Handling
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
// 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
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
// 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 for details.
Development Setup
- Clone the repository
- Install dependencies:
npm install - Run tests:
npm test - Build:
npm run build
📄 License
MIT License. See LICENSE file for details.
🆘 Support
🙏 Acknowledgments
- Built for the React Native community
- Inspired by MongoDB's query syntax
- Uses AsyncStorage for data persistence
Made with ❤️ for React Native developers
