Skip to content

Container API Reference

The Container class provides a dependency injection container for managing multiple DataSource instances with proper async initialization.


Table of Contents


Overview

The Container manages DataSource instances, ensuring:

  • Proper async initialization - No race conditions
  • Centralized management - Single source of truth
  • Status tracking - Know when DataSources are ready
  • Error handling - Graceful failure handling
  • Bridge connections - Connect DataSources together
typescript
import { Container } from 'bindra';

const container = new Container();

Constructor

typescript
constructor()

Creates a new Container instance with no arguments.

Example:

typescript
const container = new Container();

Methods

register()

Register a new DataSource with the container.

typescript
async register<T>(name: string, config: DataSourceConfig): Promise<DataSource<T>>

Parameters

  • name (string) - Unique identifier for the DataSource
  • config (DataSourceConfig) - Configuration for the DataSource

Returns

Promise<DataSource<T>> - The initialized DataSource instance

Throws

  • ConfigurationError - If a DataSource with the same name already exists

Example

typescript
interface User {
  id: number;
  name: string;
  email: string;
}

// Register a remote DataSource
const users = await container.register<User>('users', {
  url: 'https://api.example.com/users'
});

// Register a local DataSource
const products = await container.register('products', {
  data: [
    { id: 1, name: 'Product A', price: 100 },
    { id: 2, name: 'Product B', price: 200 }
  ]
});

get()

Retrieve a previously registered DataSource.

typescript
async get<T>(name: string): Promise<DataSource<T> | null>

Parameters

  • name (string) - The name of the DataSource to retrieve

Returns

Promise<DataSource<T> | null> - The DataSource instance, or null if not found or not ready

Example

typescript
// Get a registered DataSource
const users = await container.get<User>('users');

if (users) {
  await users.fetch();
  console.log(users.data);
}

getSync()

Synchronously retrieve a DataSource (only if already initialized).

typescript
getSync<T>(name: string): DataSource<T> | null

Parameters

  • name (string) - The name of the DataSource to retrieve

Returns

DataSource<T> | null - The DataSource instance, or null if not found or not ready

Example

typescript
// Only works if DataSource is already initialized
const users = container.getSync<User>('users');

if (users) {
  console.log('Users DataSource is ready');
} else {
  console.log('Users DataSource not ready yet');
}

has()

Check if a DataSource is registered.

typescript
has(name: string): boolean

Parameters

  • name (string) - The name to check

Returns

boolean - True if registered, false otherwise

Example

typescript
if (container.has('users')) {
  console.log('Users DataSource is registered');
}

getStatus()

Get the initialization status of a DataSource.

typescript
getStatus(name: string): 'initializing' | 'ready' | 'error' | null

Parameters

  • name (string) - The name of the DataSource

Returns

  • 'initializing' - DataSource is being initialized
  • 'ready' - DataSource is ready to use
  • 'error' - DataSource failed to initialize
  • null - DataSource not found

Example

typescript
const status = container.getStatus('users');

switch (status) {
  case 'initializing':
    console.log('Loading...');
    break;
  case 'ready':
    console.log('Ready!');
    break;
  case 'error':
    console.log('Failed to initialize');
    break;
  default:
    console.log('Not found');
}

initialize()

Initialize all registered DataSources at once.

typescript
async initialize(): Promise<void>

Returns

Promise<void> - Resolves when all DataSources are initialized

Throws

  • Throws if any DataSource fails to initialize

Example

typescript
const container = new Container();

// Register multiple DataSources
await container.register('users', { url: '/api/users' });
await container.register('products', { url: '/api/products' });
await container.register('orders', { url: '/api/orders' });

// Initialize all at once
await container.initialize();

// All DataSources are now ready
const users = container.getSync('users');
const products = container.getSync('products');

unregister()

Remove a DataSource from the container.

typescript
unregister(name: string): boolean

Parameters

  • name (string) - The name of the DataSource to remove

Returns

boolean - True if removed, false if not found

Example

typescript
// Remove a DataSource
const removed = container.unregister('users');

if (removed) {
  console.log('Users DataSource removed');
}

clear()

Remove all DataSources from the container.

typescript
clear(): void

Example

typescript
// Clear all DataSources
container.clear();
console.log('All DataSources removed');

list()

List all registered DataSource names.

typescript
list(): string[]

Returns

string[] - Array of DataSource names

Example

typescript
const names = container.list();
console.log('Registered DataSources:', names);
// Output: ['users', 'products', 'orders']

Usage Examples

Basic Setup

typescript
import { Container } from 'bindra';

interface User {
  id: number;
  name: string;
  email: string;
}

interface Product {
  id: number;
  name: string;
  price: number;
}

// Create container
const container = new Container();

// Register DataSources
const users = await container.register<User>('users', {
  url: 'https://api.example.com/users'
});

const products = await container.register<Product>('products', {
  url: 'https://api.example.com/products'
});

// Use DataSources
await users.fetch();
await products.fetch();

Application Initialization

typescript
// app.ts
import { Container } from 'bindra';

class Application {
  private container: Container;
  
  constructor() {
    this.container = new Container();
  }
  
  async initialize() {
    console.log('Initializing application...');
    
    // Register all DataSources
    await this.container.register('users', { url: '/api/users' });
    await this.container.register('products', { url: '/api/products' });
    await this.container.register('categories', { url: '/api/categories' });
    
    // Initialize all
    await this.container.initialize();
    
    console.log('Application initialized!');
  }
  
  getDataSource<T>(name: string) {
    return this.container.getSync<T>(name);
  }
}

// Usage
const app = new Application();
await app.initialize();

const users = app.getDataSource('users');

Error Handling

typescript
const container = new Container();

try {
  await container.register('users', {
    url: 'https://invalid-url.example.com/users'
  });
} catch (error) {
  console.error('Failed to register users DataSource:', error);
  
  // Check status
  const status = container.getStatus('users');
  console.log('Status:', status); // 'error'
}

With Bridge Connections

typescript
const container = new Container();

// Register DataSources
const users = await container.register('users', {
  url: '/api/users'
});

const orders = await container.register('orders', {
  url: '/api/orders'
});

// Create a bridge between users and orders
const bridge = container.createBridge('users', 'orders', {
  foreignKey: 'userId',
  localKey: 'id'
});

// When a user is selected, orders will automatically filter
users.goto(0);
// orders.data now contains only orders for the selected user

Status Tracking

typescript
const container = new Container();

// Register with status tracking
await container.register('users', {
  url: '/api/users'
});

// Check if ready before using
const status = container.getStatus('users');

if (status === 'ready') {
  const users = container.getSync('users');
  await users?.fetch();
} else if (status === 'initializing') {
  console.log('Please wait...');
  const users = await container.get('users'); // Wait for it
} else if (status === 'error') {
  console.error('DataSource failed to initialize');
}

Lazy Loading

typescript
class DataManager {
  private container = new Container();
  
  async getUsersDataSource() {
    // Check if already registered
    if (!this.container.has('users')) {
      await this.container.register('users', {
        url: '/api/users'
      });
    }
    
    return this.container.get('users');
  }
  
  async getProductsDataSource() {
    if (!this.container.has('products')) {
      await this.container.register('products', {
        url: '/api/products'
      });
    }
    
    return this.container.get('products');
  }
}

Best Practices

1. Use Async Registration

Always use await with register():

typescript
// ✅ Good
const users = await container.register('users', config);

// ❌ Bad - May cause race conditions
const users = container.register('users', config); // Missing await

2. Initialize Early

Initialize DataSources during app startup:

typescript
// ✅ Good - Initialize during app setup
async function initializeApp() {
  const container = new Container();
  await container.register('users', { url: '/api/users' });
  await container.register('products', { url: '/api/products' });
  await container.initialize();
  return container;
}

const container = await initializeApp();

3. Check Status Before Use

Always check if DataSource is ready:

typescript
// ✅ Good
const status = container.getStatus('users');
if (status === 'ready') {
  const users = container.getSync('users');
  // Use users
}

// ❌ Bad - May be null
const users = container.getSync('users');
users.fetch(); // Could throw if users is null

4. Handle Errors Gracefully

Wrap registration in try-catch:

typescript
// ✅ Good
try {
  await container.register('users', config);
} catch (error) {
  console.error('Failed to register users:', error);
  // Fallback logic
}

5. Use TypeScript Generics

Always specify types for type safety:

typescript
// ✅ Good
const users = await container.register<User>('users', config);
const user = users.currentRecord.value; // Typed as User | null

// ❌ Bad
const users = await container.register('users', config);
const user = users.currentRecord.value; // Typed as any


← Back to API Reference

Released under the MIT License.