npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@epicdm/flowstate-rxdb-client

v1.0.0

Published

Generic, type-safe RxDB client with replication and REST support

Downloads

71

Readme

@epic-flow/flowstate-rxdb-client

A generic, type-safe RxDB client library with dual-mode support for replication and REST API access. Built for Epic Flow applications requiring flexible data synchronization strategies.

Features

  • Dual Mode Architecture: Choose between full replication mode or lightweight REST API mode
  • Type-Safe API: Full TypeScript support with generic type parameters for compile-time safety
  • Cross-Platform: Works in browsers (IndexedDB), Node.js (filesystem), and in-memory environments
  • Reactive Queries: Built-in RxJS observables for real-time data updates (replication mode)
  • Epic Flow Preset: Pre-configured client for Epic Flow collections
  • Flexible Configuration: Customize storage, replication, and REST behavior
  • Comprehensive Error Handling: Custom error classes for different failure scenarios

When to Use Replication vs REST Mode

Replication Mode (mode: 'replication')

Use when you need:

  • Offline-first applications
  • Real-time reactive data updates
  • Local-first architecture with eventual consistency
  • Complex queries on local data
  • Reduced server load through local caching

Best for:

  • Desktop applications (Electron)
  • Mobile applications (React Native)
  • Progressive Web Apps (PWAs)
  • Applications requiring offline capabilities

REST Mode (mode: 'rest')

Use when you need:

  • Lightweight client with minimal storage
  • Server-authoritative data
  • Simple CRUD operations
  • Reduced bundle size
  • Stateless client architecture

Best for:

  • Server-side applications (Node.js services)
  • Serverless functions
  • Microservices
  • Simple data fetching without local caching

Installation

# Using yarn
yarn add @epic-flow/flowstate-rxdb-client

# Using npm
npm install @epic-flow/flowstate-rxdb-client

Peer Dependencies

The package requires the following peer dependencies:

yarn add @epic-flow/collections rxdb rxjs

Quick Start

Basic Example with Replication Mode

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import type { ReplicationConfig } from '@epic-flow/flowstate-rxdb-client';
import { TaskCollection } from '@epic-flow/collections';

// Define your collections
const collections = {
  tasks: TaskCollection,
};

// Create replication client
const config: ReplicationConfig<typeof collections> = {
  mode: 'replication',
  serverUrl: 'https://api.example.com',
  authToken: 'your-auth-token',
  domainId: 'your-domain-id',
  collections,
  storage: {
    type: 'indexeddb', // or 'memory', 'filesystem'
  },
};

const client = createRxDBClient(config);

// Connect to server and setup replication
await client.connect();

// Type-safe collection access
const tasks = await client.tasks.find({ selector: {} });
console.log(tasks);

// Reactive queries
client.tasks.find$({ selector: {} }).subscribe(tasks => {
  console.log('Tasks updated:', tasks);
});

Basic Example with REST Mode

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import type { RestConfig } from '@epic-flow/flowstate-rxdb-client';
import { TaskCollection } from '@epic-flow/collections';

// Define your collections
const collections = {
  tasks: TaskCollection,
};

// Create REST client
const config: RestConfig<typeof collections> = {
  mode: 'rest',
  serverUrl: 'https://api.example.com',
  authToken: 'your-auth-token',
  domainId: 'your-domain-id',
  collections,
  rest: {
    timeout: 5000,
    retryAttempts: 3,
  },
};

const client = createRxDBClient(config);

// Connect to server
await client.connect();

// Type-safe collection access
const tasks = await client.tasks.find({ selector: {} });
console.log(tasks);

Using Epic Flow Preset

import { createEpicFlowClient } from '@epic-flow/flowstate-rxdb-client/epic-flow';

// Create client with all Epic Flow collections pre-configured
const client = createEpicFlowClient({
  mode: 'replication',
  serverUrl: 'https://api.epicflow.com',
  authToken: 'your-auth-token',
  domainId: 'your-domain-id',
  userId: 'user-123',
  orgId: 'org-456',
});

await client.connect();

// Access all Epic Flow collections with full type safety
const projects = await client.projects.find({ selector: {} });
const tasks = await client.tasks.find({ selector: {} });
const milestones = await client.milestones.find({ selector: {} });

API Reference

Factory Function

createRxDBClient<TCollections>(config: ClientConfig<TCollections>): IRxDBClient<TCollections>

Creates an RxDB client instance based on the provided configuration. Automatically selects the appropriate implementation (ReplicationClient or RestClient) based on the mode field.

Type Parameters:

  • TCollections: Object type defining collection names as keys and their collection definitions as values

Parameters:

  • config: Client configuration (ReplicationConfig or RestConfig)

Returns:

  • IRxDBClient<TCollections>: Client instance with type-safe collection accessors

Throws:

  • ConfigurationError: If the mode is invalid or configuration is incomplete

Configuration Types

ReplicationConfig<TCollections>

Configuration for replication mode client.

interface ReplicationConfig<TCollections> {
  mode: 'replication';
  serverUrl: string;           // RxDB server URL
  authToken: string;            // Authentication token
  domainId: string;             // Domain identifier
  collections: CollectionDefinitions<TCollections>;

  // Optional fields
  userId?: string;              // User identifier for filtering
  orgId?: string;               // Organization identifier
  databaseName?: string;        // Local database name (default: 'flowstate-client')

  storage?: {
    type?: 'indexeddb' | 'memory' | 'filesystem';
    options?: Record<string, any>;  // Storage-specific options
  };

  replication?: {
    live?: boolean;             // Enable live replication (default: true)
    batchSize?: number;         // Documents per batch (default: 50)
    waitForLeadership?: boolean; // Wait for leadership election
  };
}

RestConfig<TCollections>

Configuration for REST mode client.

interface RestConfig<TCollections> {
  mode: 'rest';
  serverUrl: string;           // RxDB server URL
  authToken: string;            // Authentication token
  domainId: string;             // Domain identifier
  collections: CollectionDefinitions<TCollections>;

  // Optional fields
  userId?: string;              // User identifier for filtering
  orgId?: string;               // Organization identifier
  databaseName?: string;        // Logical database name

  rest?: {
    timeout?: number;           // Request timeout in ms (default: 5000)
    retryAttempts?: number;     // Number of retry attempts (default: 3)
  };
}

CollectionDefinitions<TCollections>

Type defining collection schemas and methods.

type CollectionDefinitions<TCollections> = {
  [K in keyof TCollections]: {
    schema: any;              // RxDB schema
    methods?: any;            // Collection methods
    statics?: any;            // Static methods
  };
};

Query<TDocument>

Query selector for finding documents.

interface Query<TDocument = any> {
  selector?: Record<string, any>;          // MongoDB-style selector
  sort?: Array<Record<string, 'asc' | 'desc'>>;  // Sort order
  limit?: number;                          // Maximum results
  skip?: number;                           // Skip first N results
}

Client Interface

IRxDBClient<TCollections>

Main client interface implemented by both ReplicationClient and RestClient.

interface IRxDBClient<TCollections> {
  // Connection management
  isConnected(): boolean;
  connect(): Promise<void>;
  disconnect(removeData?: boolean): Promise<void>;

  // Dynamic collection accessors (type-safe)
  [K: string]: any;

  // Replication manager (only in ReplicationClient)
  replication?: ReplicationManager;
}

Methods:

  • isConnected(): boolean

    • Returns whether the client is currently connected
    • Returns true if connected, false otherwise
  • connect(): Promise<void>

    • Establishes connection to the RxDB server
    • In replication mode: Creates local database and starts replication
    • In REST mode: Initializes REST clients for all collections
    • Throws ConnectionError on failure
  • disconnect(removeData?: boolean): Promise<void>

    • Disconnects from the server and cleans up resources
    • removeData: If true, removes local database (replication mode only)
    • Cancels all active replications and clears state

Dynamic Collection Properties:

Each collection defined in the configuration is accessible as a property on the client instance:

const client = createRxDBClient({ /* config */ });
await client.connect();

// Access collections directly
const tasks = await client.tasks.find({ selector: {} });
const projects = await client.projects.findOne({ selector: { id: '123' } });

CollectionAccessor Interface

Collection accessor providing CRUD operations and reactive queries.

interface CollectionAccessor<TDocument> {
  // CRUD operations
  insert(doc: TDocument): Promise<TDocument>;
  bulkInsert(docs: TDocument[]): Promise<TDocument[]>;
  findOne(query: Query<TDocument>): Promise<TDocument | null>;
  find(query: Query<TDocument>): Promise<TDocument[]>;
  update(id: string, updates: Partial<TDocument>): Promise<TDocument>;
  delete(id: string): Promise<boolean>;

  // Reactive queries (replication mode only)
  findOne$(query: Query<TDocument>): Observable<TDocument | null>;
  find$(query: Query<TDocument>): Observable<TDocument[]>;

  // Direct RxDB access (replication mode only)
  getRxCollection?(): RxCollection<TDocument>;
}

Methods:

  • insert(doc: TDocument): Promise<TDocument>

    • Inserts a single document
    • Returns the inserted document with generated fields
    • Throws error if document with same ID exists
  • bulkInsert(docs: TDocument[]): Promise<TDocument[]>

    • Inserts multiple documents in a single operation
    • Returns array of successfully inserted documents
    • Skips documents that already exist (doesn't throw)
  • findOne(query: Query<TDocument>): Promise<TDocument | null>

    • Finds a single document matching the query
    • Returns null if no document matches
    • Returns first match if multiple documents match
  • find(query: Query<TDocument>): Promise<TDocument[]>

    • Finds all documents matching the query
    • Supports sorting, limiting, and pagination
    • Returns empty array if no matches
  • update(id: string, updates: Partial<TDocument>): Promise<TDocument>

    • Updates a document by ID with partial updates
    • Returns the updated document
    • Throws error if document not found
  • delete(id: string): Promise<boolean>

    • Deletes a document by ID
    • Returns true if deleted, false if not found
  • findOne$(query: Query<TDocument>): Observable<TDocument | null> (Replication mode only)

    • Returns an observable that emits when the query result changes
    • Emits null if no document matches
    • Automatically updates when local data changes
  • find$(query: Query<TDocument>): Observable<TDocument[]> (Replication mode only)

    • Returns an observable that emits when the query results change
    • Emits empty array if no matches
    • Automatically updates when local data changes
  • getRxCollection?(): RxCollection<TDocument> (Replication mode only)

    • Returns the underlying RxDB collection
    • Use for advanced RxDB features not exposed by the wrapper
    • Returns undefined in REST mode

ReplicationManager Interface

Interface for managing replication state (replication mode only).

interface ReplicationManager {
  getState(collectionName: string): RxReplicationState<any, any> | undefined;
  awaitInitialSync(timeout?: number): Promise<void>;
  awaitPendingWrites(timeout?: number): Promise<void>;
}

Methods:

  • getState(collectionName: string): RxReplicationState<any, any> | undefined

    • Returns the replication state for a specific collection
    • Returns undefined if collection has no replication
    • Use for monitoring replication progress and errors
  • awaitInitialSync(timeout?: number): Promise<void>

    • Waits for initial replication to complete for all collections
    • timeout: Maximum wait time in milliseconds (default: 10000)
    • Resolves when all collections have completed initial sync
    • Doesn't throw on timeout, just resolves
  • awaitPendingWrites(timeout?: number): Promise<void>

    • Waits for all pending writes to be pushed to server
    • timeout: Wait time in milliseconds (default: 5000)
    • Useful before disconnecting to ensure data is saved

Error Classes

RxDBClientError

Base error class for all client errors.

class RxDBClientError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: any
  )
}

Properties:

  • message: Error description
  • code: Error code for programmatic handling
  • details: Additional error context (optional)

ConnectionError extends RxDBClientError

Thrown when connection to the server fails.

class ConnectionError extends RxDBClientError {
  constructor(message: string, details?: any)
}

Code: CONNECTION_ERROR

Common scenarios:

  • Server unreachable
  • Authentication failure
  • Network timeout
  • Invalid server URL

ReplicationError extends RxDBClientError

Thrown when replication setup or sync fails.

class ReplicationError extends RxDBClientError {
  constructor(message: string, details?: any)
}

Code: REPLICATION_ERROR

Common scenarios:

  • Replication state initialization failure
  • Sync conflicts
  • Schema version mismatch

ConfigurationError extends RxDBClientError

Thrown when client configuration is invalid.

class ConfigurationError extends RxDBClientError {
  constructor(message: string, details?: any)
}

Code: CONFIGURATION_ERROR

Common scenarios:

  • Invalid mode value
  • Missing required fields
  • Invalid collection definitions

NotSupportedError extends RxDBClientError

Thrown when attempting unsupported operations.

class NotSupportedError extends RxDBClientError {
  constructor(message: string, details?: any)
}

Code: NOT_SUPPORTED_ERROR

Common scenarios:

  • Calling reactive queries in REST mode
  • Using replication manager in REST mode
  • Platform-specific features not available

Usage Examples

Creating a Replication Client

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import { TaskCollection, ProjectCollection } from '@epic-flow/collections';

const collections = {
  tasks: TaskCollection,
  projects: ProjectCollection,
};

const client = createRxDBClient({
  mode: 'replication',
  serverUrl: 'https://api.example.com',
  authToken: 'your-jwt-token',
  domainId: 'domain-123',
  userId: 'user-456',
  collections,
  storage: {
    type: 'indexeddb',
  },
  replication: {
    live: true,
    batchSize: 100,
  },
});

await client.connect();

// Wait for initial sync
await client.replication?.awaitInitialSync();

// Use the client
const tasks = await client.tasks.find({ selector: {} });

Creating a REST Client

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import { TaskCollection } from '@epic-flow/collections';

const client = createRxDBClient({
  mode: 'rest',
  serverUrl: 'https://api.example.com',
  authToken: 'your-jwt-token',
  domainId: 'domain-123',
  collections: {
    tasks: TaskCollection,
  },
  rest: {
    timeout: 10000,
    retryAttempts: 3,
  },
});

await client.connect();

// Fetch data directly from server
const tasks = await client.tasks.find({ selector: {} });

Using Epic Flow Preset

import { createEpicFlowClient } from '@epic-flow/flowstate-rxdb-client/epic-flow';

const client = createEpicFlowClient({
  mode: 'replication',
  serverUrl: 'https://api.epicflow.com',
  authToken: 'your-auth-token',
  domainId: 'domain-123',
  userId: 'user-123',
  orgId: 'org-456',
});

await client.connect();

// Access pre-configured collections
const projects = await client.projects.find({ selector: {} });
const tasks = await client.tasks.find({ selector: {} });
const milestones = await client.milestones.find({ selector: {} });
const discussions = await client.discussions.find({ selector: {} });
const timeentries = await client.timeentries.find({ selector: {} });
const steeringdocuments = await client.steeringdocuments.find({ selector: {} });

Type-Safe Collection Operations

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import type { Task } from '@epic-flow/collections';

// Client is fully typed based on collections
const client = createRxDBClient({
  mode: 'replication',
  // ... config
});

await client.connect();

// TypeScript knows about your collections
const newTask: Task = {
  id: 'task-1',
  title: 'Complete documentation',
  status: 'in-progress',
  // ... other fields
};

// Insert with type checking
const inserted = await client.tasks.insert(newTask);

// Find with typed results
const tasks: Task[] = await client.tasks.find({
  selector: { status: 'in-progress' }
});

// Update with partial type checking
const updated = await client.tasks.update('task-1', {
  status: 'completed'
});

Reactive Queries (Replication Mode)

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'replication',
  // ... config
});

await client.connect();

// Subscribe to query results
const subscription = client.tasks.find$({
  selector: { assignedTo: 'user-123' }
}).subscribe(tasks => {
  console.log('My tasks:', tasks);
  // This will fire whenever the query results change
});

// Subscribe to single document
const taskSubscription = client.tasks.findOne$({
  selector: { id: 'task-1' }
}).subscribe(task => {
  console.log('Task updated:', task);
});

// Clean up subscriptions
subscription.unsubscribe();
taskSubscription.unsubscribe();

Handling Replication Sync

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'replication',
  // ... config
});

await client.connect();

// Wait for initial sync before showing UI
await client.replication?.awaitInitialSync(10000);

// Monitor replication state
const tasksReplication = client.replication?.getState('tasks');
if (tasksReplication) {
  tasksReplication.error$.subscribe(error => {
    console.error('Replication error:', error);
  });

  tasksReplication.active$.subscribe(active => {
    console.log('Replication active:', active);
  });
}

// Before closing app, ensure all writes are synced
window.addEventListener('beforeunload', async () => {
  await client.replication?.awaitPendingWrites(5000);
  await client.disconnect();
});

Advanced Query Examples

// Find with sorting
const sortedTasks = await client.tasks.find({
  selector: { status: 'active' },
  sort: [{ createdAt: 'desc' }],
});

// Pagination
const page1 = await client.tasks.find({
  selector: {},
  limit: 10,
  skip: 0,
});

const page2 = await client.tasks.find({
  selector: {},
  limit: 10,
  skip: 10,
});

// Complex queries
const urgentTasks = await client.tasks.find({
  selector: {
    priority: 'high',
    dueDate: { $lt: Date.now() },
    status: { $in: ['pending', 'in-progress'] },
  },
  sort: [{ dueDate: 'asc' }],
  limit: 50,
});

Bulk Operations

// Bulk insert
const newTasks = [
  { id: 'task-1', title: 'Task 1', status: 'pending' },
  { id: 'task-2', title: 'Task 2', status: 'pending' },
  { id: 'task-3', title: 'Task 3', status: 'pending' },
];

const inserted = await client.tasks.bulkInsert(newTasks);
console.log(`Inserted ${inserted.length} tasks`);

// Batch updates (with Promise.all)
const taskIds = ['task-1', 'task-2', 'task-3'];
await Promise.all(
  taskIds.map(id =>
    client.tasks.update(id, { status: 'completed' })
  )
);

Direct RxDB Access (Advanced)

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'replication',
  // ... config
});

await client.connect();

// Access underlying RxDB collection for advanced features
const rxCollection = client.tasks.getRxCollection();

if (rxCollection) {
  // Use RxDB's query builder
  const query = rxCollection
    .find()
    .where('status').eq('active')
    .where('priority').gt(5);

  const results = await query.exec();

  // Access RxDB's change stream
  const changeEvent$ = rxCollection.$.subscribe(event => {
    console.log('Collection changed:', event);
  });
}

Configuration Options

Storage Options (Replication Mode)

IndexedDB (Browser)

{
  storage: {
    type: 'indexeddb',
    options: {
      // IndexedDB-specific options
    }
  }
}

Best for: Web applications, Progressive Web Apps

Memory (Testing/Temporary)

{
  storage: {
    type: 'memory',
    options: {}
  }
}

Best for: Unit tests, temporary data, development

Filesystem (Node.js/Electron)

{
  storage: {
    type: 'filesystem',
    options: {
      basePath: './data'  // Directory for database files
    }
  }
}

Best for: Desktop applications, Node.js services

Replication Options

{
  replication: {
    // Enable live replication (continuous sync)
    live: true,

    // Number of documents per batch
    batchSize: 50,

    // Wait for leadership before starting replication
    // Useful in multi-tab scenarios
    waitForLeadership: true,
  }
}

REST Options

{
  rest: {
    // Request timeout in milliseconds
    timeout: 5000,

    // Number of retry attempts on failure
    retryAttempts: 3,
  }
}

Error Handling

Basic Error Handling

import {
  createRxDBClient,
  ConnectionError,
  ReplicationError,
  ConfigurationError,
} from '@epic-flow/flowstate-rxdb-client';

try {
  const client = createRxDBClient(config);
  await client.connect();
} catch (error) {
  if (error instanceof ConnectionError) {
    console.error('Failed to connect:', error.message);
    console.error('Details:', error.details);
    // Retry connection or show offline UI
  } else if (error instanceof ConfigurationError) {
    console.error('Invalid configuration:', error.message);
    // Fix configuration
  } else if (error instanceof ReplicationError) {
    console.error('Replication setup failed:', error.message);
    // Fall back to REST mode or show error
  } else {
    console.error('Unexpected error:', error);
  }
}

Common Error Scenarios

Connection Timeout

try {
  await client.connect();
} catch (error) {
  if (error instanceof ConnectionError) {
    // Server unreachable or slow
    // Try again with exponential backoff
    await retryWithBackoff(() => client.connect());
  }
}

Authentication Failure

try {
  await client.connect();
} catch (error) {
  if (error instanceof ConnectionError) {
    // Check if auth token is valid
    // Refresh token and retry
    const newToken = await refreshAuthToken();
    client = createRxDBClient({
      ...config,
      authToken: newToken,
    });
    await client.connect();
  }
}

Document Not Found

try {
  await client.tasks.update('non-existent-id', { status: 'completed' });
} catch (error) {
  if (error.message.includes('not found')) {
    console.error('Task does not exist');
    // Handle missing document
  }
}

Replication Errors

const client = createRxDBClient({
  mode: 'replication',
  // ... config
});

await client.connect();

// Monitor replication errors
const replicationState = client.replication?.getState('tasks');
if (replicationState) {
  replicationState.error$.subscribe(error => {
    console.error('Replication error:', error);
    // Handle sync errors
    // Show sync status in UI
  });
}

Migration Guide

Migrating from RxDBClientManager

If you're using the old RxDBClientManager class, here's how to migrate:

Before:

import { RxDBClientManager } from '@epic-flow/old-package';

const manager = new RxDBClientManager({
  serverUrl: 'https://api.example.com',
  authToken: 'token',
  domainId: 'domain',
  collections: myCollections,
});

await manager.initialize();
const tasks = await manager.getCollection('tasks').find().exec();

After:

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'replication',
  serverUrl: 'https://api.example.com',
  authToken: 'token',
  domainId: 'domain',
  collections: myCollections,
});

await client.connect();
const tasks = await client.tasks.find({ selector: {} });

Key differences:

  1. Use createRxDBClient() factory instead of new RxDBClientManager()
  2. Add mode: 'replication' to config
  3. Call connect() instead of initialize()
  4. Access collections as properties: client.tasks instead of manager.getCollection('tasks')
  5. Use simplified query interface: find({ selector: {} }) instead of RxDB's query builder

Migrating from RxDBRestClient

If you're using the old RxDBRestClient class:

Before:

import { RxDBRestClient } from '@epic-flow/old-package';

const restClient = new RxDBRestClient({
  serverUrl: 'https://api.example.com',
  authToken: 'token',
  domainId: 'domain',
});

await restClient.connect('tasks');
const tasks = await restClient.find('tasks', { status: 'active' });

After:

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'rest',
  serverUrl: 'https://api.example.com',
  authToken: 'token',
  domainId: 'domain',
  collections: myCollections,
});

await client.connect();
const tasks = await client.tasks.find({ selector: { status: 'active' } });

Key differences:

  1. Use createRxDBClient() with mode: 'rest'
  2. Define all collections upfront in config
  3. Call connect() once for all collections
  4. Access collections as properties with type safety
  5. Use MongoDB-style selectors: { selector: { status: 'active' } }

Cross-Platform Notes

Browser (IndexedDB)

const client = createRxDBClient({
  mode: 'replication',
  storage: {
    type: 'indexeddb',
  },
  // ... other config
});

Considerations:

  • IndexedDB has storage quotas (varies by browser)
  • Data persists across sessions
  • Storage is per-origin
  • Use removeData: true when disconnecting to clear storage

Node.js (Filesystem)

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';

const client = createRxDBClient({
  mode: 'replication',
  storage: {
    type: 'filesystem',
    options: {
      basePath: './data',
    },
  },
  // ... other config
});

Considerations:

  • Requires write permissions to basePath
  • Data persists in filesystem
  • Suitable for desktop apps and servers
  • Consider data backup strategies

Electron (Desktop Apps)

import { app } from 'electron';
import path from 'path';

const client = createRxDBClient({
  mode: 'replication',
  storage: {
    type: 'filesystem',
    options: {
      basePath: path.join(app.getPath('userData'), 'database'),
    },
  },
  // ... other config
});

Considerations:

  • Use app.getPath('userData') for platform-appropriate storage
  • Handle app quit events to disconnect gracefully
  • Consider multi-window scenarios with leadership election

React Native (Mobile Apps)

const client = createRxDBClient({
  mode: 'replication',
  storage: {
    type: 'filesystem',  // or use react-native-sqlite storage
  },
  // ... other config
});

Considerations:

  • May need platform-specific storage adapters
  • Consider mobile data usage for replication
  • Handle app backgrounding and foregrounding
  • Test offline scenarios thoroughly

Memory (Testing)

const client = createRxDBClient({
  mode: 'replication',
  storage: {
    type: 'memory',
  },
  // ... other config
});

Considerations:

  • Data is lost when client disconnects or process exits
  • Perfect for unit tests and integration tests
  • Fastest storage option
  • No persistence guarantees

TypeScript Support

This package is written in TypeScript and provides comprehensive type definitions.

Generic Type Parameters

import { createRxDBClient } from '@epic-flow/flowstate-rxdb-client';
import type { Task, Project } from '@epic-flow/collections';

// Define your collection types
interface MyCollections {
  tasks: Task;
  projects: Project;
}

// Client is fully typed
const client = createRxDBClient<typeof collections>({
  mode: 'replication',
  collections: {
    tasks: TaskCollection,
    projects: ProjectCollection,
  },
  // ... other config
});

await client.connect();

// TypeScript knows about these collections
const task: Task = await client.tasks.findOne({ selector: { id: '123' } });
const projects: Project[] = await client.projects.find({ selector: {} });

Type Inference

// Collections are automatically typed from config
const collections = {
  tasks: TaskCollection,
  projects: ProjectCollection,
};

const client = createRxDBClient({
  mode: 'replication',
  collections,
  // ... other config
});

// TypeScript infers the types
await client.connect();

// client.tasks is typed as CollectionAccessor<Task>
// client.projects is typed as CollectionAccessor<Project>

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch
  3. Write tests for new functionality
  4. Ensure all tests pass: yarn test
  5. Ensure type checking passes: yarn typecheck
  6. Lint your code: yarn lint
  7. Submit a pull request

License

MIT

Support

For issues and questions:


Part of the Epic Flow ecosystem - Built by Epic Digital Interactive Media LLC