Adapters Guide

NebulaDB uses adapters to handle data persistence. This modular approach allows you to choose the storage mechanism that best fits your application's needs.

Available Adapters

NebulaDB comes with several built-in adapters:

Memory Adapter

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:

LocalStorage Adapter

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:

IndexedDB Adapter

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:

FileSystem Adapter

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:

Creating Custom Adapters

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[]>> {
    return {
      users: [
        { id: '1', name: 'Alice' },
        { id: '2', name: 'Bob' }
      ],
      posts: [
        { id: '1', title: 'Hello World' }
      ]
    };
  }

  async save(data: Record<string, Document[]>): Promise<void> {
    console.log('Saving data:', data);
  }
}

const db = createDb({ adapter: new CustomAdapter() });

Example: Redis Adapter

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();
  }
}

Best Practices

Choosing the Right Adapter

Error Handling

Always 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);
}

Adapter Lifecycle

Some adapters might need cleanup when your application shuts down:

const customAdapter = new CustomAdapter();
const db = createDb({ adapter: customAdapter });

process.on('SIGINT', async () => {
  await db.save();
  await customAdapter.close();
  process.exit(0);
});