Skip to content

DataSource API Reference

The DataSource class is the core of Bindra, providing reactive data management for both local and remote data sources.

Constructor

typescript
constructor(config: DataSourceConfig<T>)

Config Options

typescript
interface DataSourceConfig<T> {
  // Data Mode
  data?: T[];                    // Local data array
  url?: string;                  // Remote API endpoint
  
  // Pagination
  pagination?: PaginationConfig;
  
  // Caching
  cache?: CacheConfig;
  
  // Real-time
  realtime?: RealtimeConfig;
  
  // Security
  security?: SecurityConfig;
  
  // Validation
  validation?: ValidationConfig<T>;
  
  // Features
  optimisticUpdates?: boolean;   // Enable optimistic updates
  retryAttempts?: number;        // Number of retry attempts
  retryDelay?: number;           // Delay between retries (ms)
}

Properties

Data Properties

data: T[] | null

The current data array. Automatically updated on fetch/create/update/delete.

typescript
const users = new DataSource<User>({ data: [] });
console.log(users.data); // []

current: T | null

The currently selected record (for navigation).

typescript
users.goto(0);
console.log(users.current); // First user

loading: boolean

Whether a network operation is in progress.

typescript
if (users.loading) {
  showSpinner();
}

Pagination Properties

currentPage: number

Current page number (1-indexed).

typescript
console.log(users.currentPage); // 1

totalPages: number | null

Total number of pages available.

typescript
console.log(`Page ${users.currentPage} of ${users.totalPages}`);

totalRecords: number | null

Total number of records across all pages.

typescript
console.log(`${users.totalRecords} users found`);

hasMore: boolean

Whether more pages are available to load.

typescript
if (users.hasMore) {
  await users.fetchMore();
}

nextCursor: string | null

Next cursor for cursor-based pagination.

typescript
console.log(`Next cursor: ${users.nextCursor}`);

Observable Properties

dataSource: Observable<T[] | null>

Observable for the data array. Subscribe to get notified of changes.

typescript
users.dataSource.subscribe((data) => {
  console.log('Data changed:', data);
});

currentRecord: Observable<T | null>

Observable for the current record.

typescript
users.currentRecord.subscribe((user) => {
  console.log('Current user:', user);
});

loadingState: Observable<boolean>

Observable for loading state.

typescript
users.loadingState.subscribe((loading) => {
  if (loading) showSpinner();
  else hideSpinner();
});

CRUD Methods

fetch(query?: QueryOptions): Promise<T[]>

Fetch data from the data source.

Parameters:

  • query - Optional query parameters (filters, sort, search, etc.)

Returns: Promise resolving to the fetched data array

Example:

typescript
// Fetch all
await users.fetch();

// Fetch with filters
await users.fetch({
  search: 'john',
  category: 'active',
  minAge: 18
});

// Fetch with local filter
await users.fetch({
  filter: (user) => user.age > 21,
  sort: 'name'
});

create(record: Partial<T>): Promise<T>

Create a new record.

Parameters:

  • record - The record to create (partial for auto-generated fields)

Returns: Promise resolving to the created record

Example:

typescript
const newUser = await users.create({
  name: 'Alice',
  email: 'alice@example.com',
  age: 25
});

console.log(newUser.id); // Auto-generated

Events Emitted:

  • created - With the created record

Validation: Throws ValidationError if validation fails.

Security: Applies sanitization if configured in security.sanitizeFields.

update(id: number, changes: Partial<T>): Promise<T>

Update an existing record.

Parameters:

  • id - Record ID
  • changes - Fields to update (partial)

Returns: Promise resolving to the updated record

Example:

typescript
const updated = await users.update(123, {
  name: 'Alice Smith',
  age: 26
});

Events Emitted:

  • updated - With the updated record

Optimistic Updates: If enabled, the UI updates immediately and rolls back on error.

delete(id: number): Promise<void>

Delete a record.

Parameters:

  • id - Record ID to delete

Returns: Promise resolving when deleted

Example:

typescript
await users.delete(123);

Events Emitted:

  • deleted - With the deleted record

Batch Operations

createBatch(records: Partial<T>[]): Promise<T[]>

Create multiple records in one operation.

Parameters:

  • records - Array of records to create

Returns: Promise resolving to array of created records

Example:

typescript
const newUsers = await users.createBatch([
  { name: 'Alice', email: 'alice@example.com' },
  { name: 'Bob', email: 'bob@example.com' },
  { name: 'Charlie', email: 'charlie@example.com' }
]);

console.log(`Created ${newUsers.length} users`);

Benefits:

  • Single API call (vs multiple)
  • Atomic operation
  • Better performance

updateBatch(updates: BatchUpdate<T>[]): Promise<T[]>

Update multiple records in one operation.

Parameters:

  • updates - Array of { id, changes } objects

Returns: Promise resolving to array of updated records

Example:

typescript
const updated = await users.updateBatch([
  { id: 1, changes: { status: 'active' } },
  { id: 2, changes: { status: 'active' } },
  { id: 3, changes: { status: 'inactive' } }
]);

deleteBatch(ids: number[]): Promise<void>

Delete multiple records in one operation.

Parameters:

  • ids - Array of IDs to delete

Returns: Promise resolving when deleted

Example:

typescript
await users.deleteBatch([1, 2, 3, 4, 5]);

Pagination Methods

fetchMore(): Promise<T[]>

Load the next page of data (appends to existing data).

Returns: Promise resolving to the fetched data

Example:

typescript
// Load page 1
await users.fetch();

// Load page 2 (appends to page 1)
await users.fetchMore();

// Load page 3 (appends to pages 1 & 2)
await users.fetchMore();

console.log(users.data.length); // All 3 pages

Use Cases:

  • Infinite scroll
  • Load more button
  • Progressive data loading

fetchPage(page: number): Promise<T[]>

Load a specific page (replaces existing data).

Parameters:

  • page - Page number (1-indexed)

Returns: Promise resolving to the fetched data

Example:

typescript
// Jump to page 5
await users.fetchPage(5);

console.log(users.currentPage); // 5

Use Cases:

  • Traditional pagination controls
  • Jump to specific page

Query Methods

query(options: QueryOptions): Promise<T[]>

Query data with filters, sort, and limits.

Parameters:

typescript
interface QueryOptions {
  filter?: (item: T) => boolean;  // Local filter function
  sort?: keyof T | ((a: T, b: T) => number);  // Sort field or comparator
  limit?: number;  // Max results
  offset?: number;  // Skip first N results
  [key: string]: any;  // Additional query params for remote API
}

Example:

typescript
// Local mode: filter in memory
const activeUsers = await users.query({
  filter: (u) => u.status === 'active',
  sort: 'name',
  limit: 10
});

// Remote mode: sends as query string
const results = await users.query({
  status: 'active',
  minAge: 18,
  sort: 'created_at',
  order: 'desc'
});
// GET /api/users?status=active&minAge=18&sort=created_at&order=desc

next(): void

Move to the next record.

typescript
users.goto(0);  // First record
users.next();   // Second record
users.next();   // Third record

prev(): void

Move to the previous record.

typescript
users.goto(5);  // 6th record
users.prev();   // 5th record

goto(index: number): void

Move to a specific record by index.

typescript
users.goto(0);   // First
users.goto(10);  // 11th
users.goto(users.data.length - 1);  // Last

findById(id: number): T | undefined

Find a record by its ID.

typescript
const user = users.findById(123);
if (user) {
  console.log(user.name);
}

Caching Methods

clearCache(): void

Clear all cached data.

typescript
users.clearCache();
// Next fetch() will hit the API

invalidateCache(key?: string): void

Invalidate cache for a specific key or all keys.

typescript
// Invalidate all caches
users.invalidateCache();

// Invalidate specific query cache
users.invalidateCache('GET:/api/users?status=active');

WebSocket Methods

connectWebSocket(): void

Connect to the WebSocket server (if realtime is enabled).

typescript
users.connectWebSocket();

Auto-connect: Automatically called if realtime.enabled = true.

disconnectWebSocket(): void

Disconnect from the WebSocket server.

typescript
users.disconnectWebSocket();

Cleanup: Always call on component unmount to avoid memory leaks.

sendWebSocketMessage(data: any): void

Send a message through the WebSocket connection.

typescript
users.sendWebSocketMessage({
  type: 'subscribe',
  channel: 'users'
});

Event Methods

on(event: string, handler: Function): void

Register an event listener.

typescript
users.on('created', (user) => {
  console.log('User created:', user);
});

off(event: string, handler?: Function): void

Remove an event listener.

typescript
const handler = (user) => console.log(user);
users.on('created', handler);
users.off('created', handler);

// Remove all listeners for an event
users.off('created');

emit(event: string, data?: any): void

Emit a custom event.

typescript
users.emit('customEvent', { foo: 'bar' });

Events

CRUD Events

  • created - (record: T) => void - Record created
  • updated - (record: T) => void - Record updated
  • deleted - (record: T) => void - Record deleted
  • fetched - (records: T[]) => void - Data fetched
  • error - (error: Error) => void - Error occurred

Real-time Events

  • ws:connected - () => void - WebSocket connected
  • ws:disconnected - () => void - WebSocket disconnected
  • ws:error - (error: Error) => void - WebSocket error
  • realtime:message - (message: any) => void - Custom WebSocket message
  • currentChanged - (record: T | null) => void - Current record changed

Error Handling

All async methods can throw:

  • ValidationError - Validation failed

    typescript
    try {
      await users.create({ name: '' });
    } catch (error) {
      if (error instanceof ValidationError) {
        console.log(error.validationErrors);
      }
    }
  • NetworkError - Network request failed

    typescript
    try {
      await users.fetch();
    } catch (error) {
      if (error instanceof NetworkError) {
        console.log('Network error:', error.message);
      }
    }
  • NotFoundError - Record not found

    typescript
    try {
      await users.update(999, { name: 'Test' });
    } catch (error) {
      if (error instanceof NotFoundError) {
        console.log('User not found');
      }
    }

Learn More

Released under the MIT License.