DataSource API Reference
The DataSource class is the core of Bindra, providing reactive data management for both local and remote data sources.
Constructor
constructor(config: DataSourceConfig<T>)Config Options
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.
const users = new DataSource<User>({ data: [] });
console.log(users.data); // []current: T | null
The currently selected record (for navigation).
users.goto(0);
console.log(users.current); // First userloading: boolean
Whether a network operation is in progress.
if (users.loading) {
showSpinner();
}Pagination Properties
currentPage: number
Current page number (1-indexed).
console.log(users.currentPage); // 1totalPages: number | null
Total number of pages available.
console.log(`Page ${users.currentPage} of ${users.totalPages}`);totalRecords: number | null
Total number of records across all pages.
console.log(`${users.totalRecords} users found`);hasMore: boolean
Whether more pages are available to load.
if (users.hasMore) {
await users.fetchMore();
}nextCursor: string | null
Next cursor for cursor-based pagination.
console.log(`Next cursor: ${users.nextCursor}`);Observable Properties
dataSource: Observable<T[] | null>
Observable for the data array. Subscribe to get notified of changes.
users.dataSource.subscribe((data) => {
console.log('Data changed:', data);
});currentRecord: Observable<T | null>
Observable for the current record.
users.currentRecord.subscribe((user) => {
console.log('Current user:', user);
});loadingState: Observable<boolean>
Observable for loading state.
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:
// 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:
const newUser = await users.create({
name: 'Alice',
email: 'alice@example.com',
age: 25
});
console.log(newUser.id); // Auto-generatedEvents 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 IDchanges- Fields to update (partial)
Returns: Promise resolving to the updated record
Example:
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:
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:
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:
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:
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:
// 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 pagesUse 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:
// Jump to page 5
await users.fetchPage(5);
console.log(users.currentPage); // 5Use Cases:
- Traditional pagination controls
- Jump to specific page
Query Methods
query(options: QueryOptions): Promise<T[]>
Query data with filters, sort, and limits.
Parameters:
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:
// 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=descNavigation Methods
next(): void
Move to the next record.
users.goto(0); // First record
users.next(); // Second record
users.next(); // Third recordprev(): void
Move to the previous record.
users.goto(5); // 6th record
users.prev(); // 5th recordgoto(index: number): void
Move to a specific record by index.
users.goto(0); // First
users.goto(10); // 11th
users.goto(users.data.length - 1); // LastfindById(id: number): T | undefined
Find a record by its ID.
const user = users.findById(123);
if (user) {
console.log(user.name);
}Caching Methods
clearCache(): void
Clear all cached data.
users.clearCache();
// Next fetch() will hit the APIinvalidateCache(key?: string): void
Invalidate cache for a specific key or all keys.
// 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).
users.connectWebSocket();Auto-connect: Automatically called if realtime.enabled = true.
disconnectWebSocket(): void
Disconnect from the WebSocket server.
users.disconnectWebSocket();Cleanup: Always call on component unmount to avoid memory leaks.
sendWebSocketMessage(data: any): void
Send a message through the WebSocket connection.
users.sendWebSocketMessage({
type: 'subscribe',
channel: 'users'
});Event Methods
on(event: string, handler: Function): void
Register an event listener.
users.on('created', (user) => {
console.log('User created:', user);
});off(event: string, handler?: Function): void
Remove an event listener.
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.
users.emit('customEvent', { foo: 'bar' });Events
CRUD Events
created-(record: T) => void- Record createdupdated-(record: T) => void- Record updateddeleted-(record: T) => void- Record deletedfetched-(records: T[]) => void- Data fetchederror-(error: Error) => void- Error occurred
Real-time Events
ws:connected-() => void- WebSocket connectedws:disconnected-() => void- WebSocket disconnectedws:error-(error: Error) => void- WebSocket errorrealtime:message-(message: any) => void- Custom WebSocket message
Navigation Events
currentChanged-(record: T | null) => void- Current record changed
Error Handling
All async methods can throw:
ValidationError- Validation failedtypescripttry { await users.create({ name: '' }); } catch (error) { if (error instanceof ValidationError) { console.log(error.validationErrors); } }NetworkError- Network request failedtypescripttry { await users.fetch(); } catch (error) { if (error instanceof NetworkError) { console.log('Network error:', error.message); } }NotFoundError- Record not foundtypescripttry { await users.update(999, { name: 'Test' }); } catch (error) { if (error instanceof NotFoundError) { console.log('User not found'); } }