NebulaDB uses adapters to handle data persistence. This modular approach allows you to choose the storage mechanism that best fits your application's needs.
NebulaDB comes with several built-in adapters:
The Memory Adapter stores data in memory only. Data is lost when the application restarts.
import { createDb } from '@nebula/core';
import { MemoryAdapter } from '@nebula/adapter-memory';
const db = createDb({
adapter: new MemoryAdapter()
});
Use cases:
The LocalStorage Adapter persists data to the browser's localStorage.
import { createDb } from '@nebula/core';
import { LocalStorageAdapter } from '@nebula/adapter-localstorage';
const db = createDb({
adapter: new LocalStorageAdapter('my-app-data')
});
Use cases:
The IndexedDB Adapter persists data to the browser's IndexedDB, which can handle larger datasets.
import { createDb } from '@nebula/core';
import { IndexedDBAdapter } from '@nebula/adapter-indexeddb';
const db = createDb({
adapter: new IndexedDBAdapter('my-app-db', 'collections', 1)
});
Use cases:
The FileSystem Adapter persists data to the file system in Node.js environments.
import { createDb } from '@nebula/core';
import { FileSystemAdapter } from '@nebula/adapter-filesystem';
import path from 'path';
const db = createDb({
adapter: new FileSystemAdapter(path.join(__dirname, 'data.json'))
});
Use cases:
You can create your own adapters by implementing the Adapter
interface:
import { Adapter, Document } from '@nebula/core';
class CustomAdapter implements Adapter {
async load(): Promise<Record<string, Document[]>> {
// Load data from your storage mechanism
// Return an object where keys are collection names and values are arrays of documents
return {
users: [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' }
],
posts: [
{ id: '1', title: 'Hello World' }
]
};
}
async save(data: Record<string, Document[]>): Promise<void> {
// Save data to your storage mechanism
// 'data' is an object where keys are collection names and values are arrays of documents
console.log('Saving data:', data);
}
}
// Use your custom adapter
const db = createDb({
adapter: new CustomAdapter()
});
Here's an example of a custom adapter that uses Redis for storage:
import { Adapter, Document } from '@nebula/core';
import Redis from 'ioredis';
export class RedisAdapter implements Adapter {
private redis: Redis;
private key: string;
constructor(redisOptions: Redis.RedisOptions = {}, key: string = 'nebula-db') {
this.redis = new Redis(redisOptions);
this.key = key;
}
async load(): Promise<Record<string, Document[]>> {
try {
const data = await this.redis.get(this.key);
return data ? JSON.parse(data) : {};
} catch (error) {
console.error('Failed to load data from Redis:', error);
return {};
}
}
async save(data: Record<string, Document[]>): Promise<void> {
try {
await this.redis.set(this.key, JSON.stringify(data));
} catch (error) {
console.error('Failed to save data to Redis:', error);
throw error;
}
}
async close(): Promise<void> {
await this.redis.quit();
}
}
LocalStorageAdapter
for simple apps or IndexedDBAdapter
for more complex onesFileSystemAdapter
or a custom adapter for your databaseMemoryAdapter
for fast, isolated testsAlways handle errors that might occur during loading or saving:
try {
await db.save();
console.log('Data saved successfully');
} catch (error) {
console.error('Failed to save data:', error);
// Handle the error appropriately
}
Some adapters might need cleanup when your application shuts down:
// Example with a custom adapter that has a close method
const customAdapter = new CustomAdapter();
const db = createDb({ adapter: customAdapter });
// When your application is shutting down
process.on('SIGINT', async () => {
await db.save(); // Save any pending changes
await customAdapter.close(); // Close connections
process.exit(0);
});