Official ObjectStack data adapter for Object UI.
This package provides the ObjectStackAdapter class, which connects Object UI's universal DataSource interface with the @objectstack/client SDK.
This enables strictly typed, metadata-driven UI components to communicate seamlessly with ObjectStack backends (Steedos, Salesforce, etc.).
npm install @object-ui/data-objectstack @objectstack/clientimport { createObjectStackAdapter } from '@object-ui/data-objectstack';
import { SchemaRenderer } from '@object-ui/react';
// 1. Create the adapter
const dataSource = createObjectStackAdapter({
baseUrl: 'https://api.example.com',
token: 'your-api-token' // Optional if effectively handling auth elsewhere
});
// 2. Pass to the Renderer
function App() {
return (
<SchemaRenderer
schema={mySchema}
dataSource={dataSource}
/>
);
}const dataSource = createObjectStackAdapter({
baseUrl: 'https://api.example.com',
token: 'your-api-token',
// Configure metadata cache
cache: {
maxSize: 100, // Maximum number of cached schemas (default: 100)
ttl: 5 * 60 * 1000 // Time to live in ms (default: 5 minutes)
},
// Configure auto-reconnect
autoReconnect: true, // Enable auto-reconnect (default: true)
maxReconnectAttempts: 5, // Max reconnection attempts (default: 3)
reconnectDelay: 2000 // Initial delay between reconnects in ms (default: 1000)
});- ✅ CRUD Operations: Implements
find,findOne,create,update,delete. - ✅ Metadata Caching: Automatic LRU caching of schema metadata with TTL expiration.
- ✅ Metadata Fetching: Implements
getObjectSchemato power auto-generated forms and grids. - ✅ Query Translation: Converts Object UI's OData-like query parameters to ObjectStack's native query format.
- ✅ Bulk Operations: Supports optimized batch create/update/delete with detailed error reporting.
- ✅ Error Handling: Comprehensive error hierarchy with unique error codes and debugging details.
- ✅ Connection Monitoring: Real-time connection state tracking with event listeners.
- ✅ Auto-Reconnect: Automatic reconnection with exponential backoff on connection failures.
- ✅ Batch Progress: Progress events for tracking bulk operation status.
The adapter includes built-in metadata caching to improve performance when fetching schemas:
// Get cache statistics
const stats = dataSource.getCacheStats();
console.log(`Cache hit rate: ${stats.hitRate * 100}%`);
console.log(`Cache size: ${stats.size}/${stats.maxSize}`);
// Manually invalidate cache entries
dataSource.invalidateCache('users'); // Invalidate specific schema
dataSource.invalidateCache(); // Invalidate all cached schemas
// Clear cache and statistics
dataSource.clearCache();- LRU Eviction: Automatically evicts least recently used entries when cache is full
- TTL Expiration: Entries expire after the configured time-to-live from creation (default: 5 minutes)
- Note: TTL is fixed from creation time, not sliding based on access
- Memory Limits: Configurable maximum cache size (default: 100 entries)
- Concurrent Access: Handles async operations safely. Note that concurrent requests for the same uncached key may result in multiple fetcher calls.
The adapter provides real-time connection state monitoring with automatic reconnection:
// Monitor connection state changes
const unsubscribe = dataSource.onConnectionStateChange((event) => {
console.log('Connection state:', event.state);
console.log('Timestamp:', new Date(event.timestamp));
if (event.error) {
console.error('Connection error:', event.error);
}
});
// Check current connection state
console.log(dataSource.getConnectionState()); // 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error'
// Check if connected
if (dataSource.isConnected()) {
console.log('Adapter is connected');
}
// Unsubscribe from events when done
unsubscribe();disconnected- Not connected to serverconnecting- Attempting initial connectionconnected- Successfully connectedreconnecting- Attempting to reconnect after failureerror- Connection failed (check event.error for details)
The adapter automatically attempts to reconnect on connection failures:
- Exponential Backoff: Delay increases with each attempt (delay × 2^(attempts-1))
- Configurable Attempts: Set
maxReconnectAttempts(default: 3) - Configurable Delay: Set
reconnectDelayfor initial delay (default: 1000ms) - Automatic: Enabled by default, disable with
autoReconnect: false
Track progress of bulk operations in real-time:
// Monitor batch operation progress
const unsubscribe = dataSource.onBatchProgress((event) => {
console.log(`${event.operation}: ${event.percentage.toFixed(1)}%`);
console.log(`Completed: ${event.completed}/${event.total}`);
console.log(`Failed: ${event.failed}`);
});
// Perform bulk operation
const users = await dataSource.bulk('users', 'create', largeDataset);
// Unsubscribe when done
unsubscribe();operation- Operation type ('create' | 'update' | 'delete')total- Total number of itemscompleted- Number of successfully completed itemsfailed- Number of failed itemspercentage- Completion percentage (0-100)
The adapter provides a comprehensive error hierarchy for better error handling:
import {
ObjectStackError, // Base error class
MetadataNotFoundError, // Schema/metadata not found (404)
BulkOperationError, // Bulk operation failures with partial results
ConnectionError, // Network/connection errors (503/504)
AuthenticationError, // Authentication failures (401/403)
ValidationError, // Data validation errors (400)
} from '@object-ui/data-objectstack';try {
const schema = await dataSource.getObjectSchema('users');
} catch (error) {
if (error instanceof MetadataNotFoundError) {
console.error(`Schema not found: ${error.details.objectName}`);
} else if (error instanceof ConnectionError) {
console.error(`Connection failed to: ${error.url}`);
} else if (error instanceof AuthenticationError) {
console.error('Authentication required');
}
// All errors have consistent structure
console.error({
code: error.code,
message: error.message,
statusCode: error.statusCode,
details: error.details
});
}Bulk operations provide detailed error reporting with partial success information:
try {
await dataSource.bulk('users', 'update', records);
} catch (error) {
if (error instanceof BulkOperationError) {
const summary = error.getSummary();
console.log(`${summary.successful} succeeded, ${summary.failed} failed`);
console.log(`Failure rate: ${summary.failureRate * 100}%`);
// Inspect individual failures
summary.errors.forEach(({ index, error }) => {
console.error(`Record ${index} failed:`, error);
});
}
}All errors include unique error codes for programmatic handling:
METADATA_NOT_FOUND- Schema/metadata not foundBULK_OPERATION_ERROR- Bulk operation failureCONNECTION_ERROR- Connection/network errorAUTHENTICATION_ERROR- Authentication failureVALIDATION_ERROR- Data validation errorUNSUPPORTED_OPERATION- Unsupported operationNOT_FOUND- Resource not foundUNKNOWN_ERROR- Unknown error
The adapter supports optimized batch operations with automatic fallback:
// Batch create
const newUsers = await dataSource.bulk('users', 'create', [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
]);
// Batch update (uses updateMany if available, falls back to individual updates)
const updated = await dataSource.bulk('users', 'update', [
{ id: '1', name: 'Alice Smith' },
{ id: '2', name: 'Bob Jones' },
]);
// Batch delete
await dataSource.bulk('users', 'delete', [
{ id: '1' },
{ id: '2' },
]);- Automatically uses
createMany,updateMany,deleteManywhen available - Falls back to individual operations with detailed error tracking
- Provides partial success reporting for resilient error handling
- Atomic operations where supported by the backend
new ObjectStackAdapter(config: {
baseUrl: string;
token?: string;
fetch?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
cache?: {
maxSize?: number;
ttl?: number;
};
autoReconnect?: boolean;
maxReconnectAttempts?: number;
reconnectDelay?: number;
})connect()- Establish connection to ObjectStack serverfind(resource, params?)- Query multiple recordsfindOne(resource, id, params?)- Get a single record by IDcreate(resource, data)- Create a new recordupdate(resource, id, data)- Update an existing recorddelete(resource, id)- Delete a recordbulk(resource, operation, data)- Batch operations (create/update/delete)getObjectSchema(objectName)- Get schema metadata (cached)getCacheStats()- Get cache statisticsinvalidateCache(key?)- Invalidate cache entriesclearCache()- Clear all cache entriesgetClient()- Access underlying ObjectStack clientgetConnectionState()- Get current connection stateisConnected()- Check if adapter is connectedonConnectionStateChange(listener)- Subscribe to connection state changes (returns unsubscribe function)onBatchProgress(listener)- Subscribe to batch operation progress (returns unsubscribe function)
- Enable Caching: Use default cache settings for optimal performance
- Handle Errors: Use typed error handling for better user experience
- Batch Operations: Use bulk methods for large datasets
- Monitor Cache: Check cache hit rates in production
- Invalidate Wisely: Clear cache after schema changes
- Connection Monitoring: Subscribe to connection state changes for better UX
- Auto-Reconnect: Use default auto-reconnect settings for resilient applications
- Batch Progress: Monitor progress for long-running bulk operations
// Error: MetadataNotFoundError
// Solution: Verify object name and ensure schema exists on server
const schema = await dataSource.getObjectSchema('correct_object_name');// Error: ConnectionError
// Solution: Check baseUrl and network connectivity
const dataSource = createObjectStackAdapter({
baseUrl: 'https://correct-url.example.com',
token: 'valid-token'
});// Clear cache if stale data is being returned
dataSource.clearCache();
// Or invalidate specific entries
dataSource.invalidateCache('users');MIT