Skip to content

Type Definitions API Reference

Complete TypeScript type definitions and interfaces for Bindra.


Table of Contents


Overview

Bindra is fully typed with TypeScript, providing comprehensive type safety for all operations. All types are exported from the main package:

typescript
import type {
  // Core
  DataSourceConfig,
  QueryOptions,
  Field,
  Permissions,
  
  // Configuration
  RetryConfig,
  CacheConfig,
  PaginationConfig,
  RealtimeConfig,
  SecurityConfig,
  
  // Validation
  FieldConfig,
  FieldValidation,
  
  // Observable
  Signal
} from 'bindra';

Core Types

DataSourceConfig<T>

Main configuration interface for creating a DataSource instance.

typescript
interface DataSourceConfig<T extends Record<string, any> = any> {
  /** Remote API endpoint URL */
  url?: string | null;
  
  /** Local data array (for client-side data) */
  data?: T[] | null;
  
  /** Retry configuration for failed requests */
  retry?: RetryConfig;
  
  /** Field configurations with validation rules */
  fields?: FieldConfig<T>[];
  
  /** Cache configuration */
  cache?: CacheConfig;
  
  /** Default page size for pagination */
  pageSize?: number;
  
  /** Real-time updates via WebSocket */
  realtime?: RealtimeConfig;
  
  /** Security configuration */
  security?: SecurityConfig;
}

Type Parameter

  • T - The record type managed by the DataSource (must extend Record<string, any>)

Example

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

const config: DataSourceConfig<User> = {
  url: 'https://api.example.com/users',
  cache: { enabled: true, ttl: 300000 },
  pageSize: 20
};

const users = new DataSource<User>(config);

QueryOptions<T>

Options for querying and filtering data.

typescript
interface QueryOptions<T = any> {
  /** Filter function or object to match records */
  filter?: ((record: T) => boolean) | object | null;
  
  /** Sort field name or comparison function */
  sort?: string | ((a: T, b: T) => number) | null;
  
  /** Maximum number of records to return */
  limit?: number | null;
}

Examples

typescript
// Function filter
await users.fetch({
  filter: (user) => user.age >= 18,
  sort: 'name',
  limit: 10
});

// Object filter (for remote DataSources)
await users.fetch({
  filter: { status: 'active' },
  sort: 'createdAt',
  limit: 50
});

// Custom sort function
await users.fetch({
  sort: (a, b) => b.age - a.age // Sort by age descending
});

Field

Field metadata describing a data field.

typescript
interface Field {
  /** Field name */
  name: string;
  
  /** JavaScript type of the field */
  type: string;
}

Usage

Fields are automatically detected for local DataSources or fetched from remote API config endpoints.

typescript
const fields = dataSource.fields;
// [{ name: 'id', type: 'number' }, { name: 'name', type: 'string' }, ...]

Permissions

Permission flags for CRUD operations.

typescript
interface Permissions {
  /** Allow creating new records */
  allowInsert?: boolean;
  
  /** Allow updating existing records */
  allowUpdate?: boolean;
  
  /** Allow deleting records */
  allowDelete?: boolean;
}

Example

typescript
const permissions = dataSource.permissions;
// { allowInsert: true, allowUpdate: true, allowDelete: false }

if (permissions.allowDelete) {
  await dataSource.delete(userId);
}

Configuration Types

RetryConfig

Configuration for automatic retry of failed requests.

typescript
interface RetryConfig {
  /** Maximum number of retry attempts (default: 3) */
  maxRetries?: number;
  
  /** Initial delay between retries in milliseconds (default: 1000) */
  retryDelay?: number;
  
  /** Multiplier for exponential backoff (default: 2) */
  backoffMultiplier?: number;
}

Example

typescript
const config: DataSourceConfig<User> = {
  url: '/api/users',
  retry: {
    maxRetries: 5,
    retryDelay: 2000,      // Start with 2 seconds
    backoffMultiplier: 1.5  // 2s, 3s, 4.5s, 6.75s, 10.125s
  }
};

CacheConfig

Configuration for response caching.

typescript
interface CacheConfig {
  /** Enable/disable caching */
  enabled: boolean;
  
  /** Time to live in milliseconds (default: no expiration) */
  ttl?: number;
  
  /** Maximum cache size (default: unlimited) */
  maxSize?: number;
}

Example

typescript
const config: DataSourceConfig<User> = {
  url: '/api/users',
  cache: {
    enabled: true,
    ttl: 300000,     // 5 minutes
    maxSize: 1000    // Max 1000 cached items
  }
};

PaginationConfig

Pagination state and configuration.

typescript
interface PaginationConfig {
  /** Number of records per page */
  pageSize: number;
  
  /** Current page number (1-indexed) */
  currentPage: number;
  
  /** Total number of records (optional) */
  totalRecords?: number;
  
  /** Total number of pages (optional) */
  totalPages?: number;
}

Usage

typescript
const pagination = dataSource.pagination.get();
console.log(pagination);
// {
//   pageSize: 20,
//   currentPage: 1,
//   totalRecords: 142,
//   totalPages: 8
// }

// Update page size
dataSource.pagination.set({
  ...pagination,
  pageSize: 50
});

RealtimeConfig

Configuration for WebSocket-based real-time updates.

typescript
interface RealtimeConfig {
  /** Enable/disable real-time updates */
  enabled: boolean;
  
  /** WebSocket server URL */
  url?: string;
  
  /** Automatically reconnect on disconnect */
  reconnect?: boolean;
  
  /** Interval between reconnection attempts in milliseconds */
  reconnectInterval?: number;
}

Example

typescript
const config: DataSourceConfig<Message> = {
  url: '/api/messages',
  realtime: {
    enabled: true,
    url: 'wss://api.example.com/ws',
    reconnect: true,
    reconnectInterval: 5000
  }
};

SecurityConfig

Security and sanitization configuration.

typescript
interface SecurityConfig {
  /** CSRF token for request authentication */
  csrfToken?: string;
  
  /** Header name for CSRF token */
  csrfHeader?: string;
  
  /** Fields to sanitize (remove HTML/scripts) */
  sanitizeFields?: (keyof any)[];
}

Example

typescript
const config: DataSourceConfig<Post> = {
  url: '/api/posts',
  security: {
    csrfToken: document.querySelector('meta[name="csrf"]')?.content,
    csrfHeader: 'X-CSRF-Token',
    sanitizeFields: ['title', 'content', 'bio']
  }
};

Validation Types

FieldConfig<T>

Complete field configuration with validation rules.

typescript
interface FieldConfig<T extends Record<string, any>> {
  /** Field name (must be a key of T) */
  name: keyof T;
  
  /** Display label for UI */
  label?: string;
  
  /** Validation rules */
  validation?: FieldValidation<T>;
  
  /** Default value when creating records */
  defaultValue?: any;
  
  /** Computed field function */
  computed?: (record: T) => any;
  
  /** Read-only field (won't be validated or saved) */
  readOnly?: boolean;
  
  /** Transform value before validation */
  transform?: (value: any) => any;
}

Example

typescript
interface User {
  id: number;
  email: string;
  age: number;
  status: string;
  fullName: string;
}

const fields: FieldConfig<User>[] = [
  {
    name: 'email',
    label: 'Email Address',
    validation: {
      required: true,
      type: 'email'
    }
  },
  {
    name: 'age',
    label: 'Age',
    validation: {
      type: 'number',
      min: 18,
      max: 120
    }
  },
  {
    name: 'status',
    defaultValue: 'active'
  },
  {
    name: 'fullName',
    computed: (user) => `${user.firstName} ${user.lastName}`,
    readOnly: true
  }
];

FieldValidation<T>

Validation rules for a single field.

typescript
interface FieldValidation<T extends Record<string, any>> {
  /** Whether the field is required */
  required?: boolean;
  
  /** Data type validation */
  type?: 'string' | 'number' | 'boolean' | 'date' | 'email' | 'url' | 'array' | 'object';
  
  /** Minimum value (for numbers) or length (for strings/arrays) */
  min?: number;
  
  /** Maximum value (for numbers) or length (for strings/arrays) */
  max?: number;
  
  /** RegEx pattern for string validation */
  pattern?: RegExp;
  
  /** Custom validation function */
  custom?: (value: any, record: Partial<T>) => boolean | string | Promise<boolean | string>;
  
  /** Error message override */
  message?: string;
}

Examples

typescript
// Required field
const emailField: FieldConfig<User> = {
  name: 'email',
  validation: { required: true }
};

// Type validation
const ageField: FieldConfig<User> = {
  name: 'age',
  validation: { type: 'number', min: 0, max: 150 }
};

// Pattern validation
const phoneField: FieldConfig<User> = {
  name: 'phone',
  validation: {
    pattern: /^\d{3}-\d{3}-\d{4}$/,
    message: 'Phone must be in format: XXX-XXX-XXXX'
  }
};

// Custom validation
const usernameField: FieldConfig<User> = {
  name: 'username',
  validation: {
    custom: async (value) => {
      const available = await checkUsernameAvailable(value);
      return available || 'Username already taken';
    }
  }
};

// Multiple rules
const passwordField: FieldConfig<User> = {
  name: 'password',
  validation: {
    required: true,
    type: 'string',
    min: 8,
    pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
    message: 'Password must be at least 8 characters with uppercase, lowercase, and number'
  }
};

Observable Types

Signal<T>

Reactive signal interface for state management.

typescript
interface Signal<T> {
  /** Get the current value */
  get: () => T;
  
  /** Set a new value (triggers subscribers) */
  set: (newValue: T) => void;
  
  /** Subscribe to value changes */
  subscribe: (fn: (value: T) => void) => UnsubscribeFunction;
}

type UnsubscribeFunction = () => void;

Example

typescript
import { createSignal } from 'bindra';

const count = createSignal(0);

// Get value
console.log(count.get()); // 0

// Subscribe to changes
const unsubscribe = count.subscribe((value) => {
  console.log('Count changed:', value);
});

// Update value (triggers subscriber)
count.set(1); // Logs: "Count changed: 1"

// Cleanup
unsubscribe();

Middleware Types

CRUDContext

Context object passed to CRUD middleware handlers.

typescript
interface CRUDContext {
  /** The record being operated on */
  record?: any;
  
  /** The key/id for update/delete operations */
  key?: any;
  
  /** Changes being applied (for updates) */
  changes?: any;
  
  /** Reference to the DataSource instance */
  ds?: any;
}

Example

typescript
dataSource.middleware.useBefore('create', async (context: CRUDContext) => {
  console.log('Creating record:', context.record);
  console.log('DataSource:', context.ds.url);
  
  // Modify record before creation
  context.record.createdAt = new Date();
});

dataSource.middleware.useAfter('update', async (context: CRUDContext) => {
  console.log('Updated record:', context.record);
  console.log('Key:', context.key);
  console.log('Changes:', context.changes);
});

Middleware Handler Types

typescript
// Generic middleware for standalone validation
type GenericMiddlewareHandler = (
  value: any,
  next: (processedValue: any) => void
) => void;

// CRUD operation middleware
type CRUDMiddlewareHandler = (
  context: CRUDContext
) => void | Promise<void>;

Utility Types

Batch Operation Types

typescript
// Batch update
interface BatchUpdate<T> {
  id: number;
  changes: Partial<T>;
}

// Example
const updates: BatchUpdate<User>[] = [
  { id: 1, changes: { status: 'active' } },
  { id: 2, changes: { status: 'inactive' } }
];

await dataSource.updateBatch(updates);

Event Types

typescript
// Event handler function
type EventHandler = (...args: any[]) => void;

// Unsubscribe function
type UnsubscribeFunction = () => void;

Usage Examples

Complete Type-Safe DataSource

typescript
import { DataSource } from 'bindra';
import type {
  DataSourceConfig,
  FieldConfig,
  QueryOptions
} from 'bindra';

// Define your model
interface Product {
  id: number;
  name: string;
  price: number;
  category: string;
  inStock: boolean;
  createdAt: Date;
}

// Define field configurations
const fields: FieldConfig<Product>[] = [
  {
    name: 'name',
    label: 'Product Name',
    validation: {
      required: true,
      type: 'string',
      min: 3,
      max: 100
    }
  },
  {
    name: 'price',
    label: 'Price',
    validation: {
      required: true,
      type: 'number',
      min: 0
    }
  },
  {
    name: 'category',
    label: 'Category',
    validation: {
      required: true,
      custom: (value) => {
        const validCategories = ['electronics', 'clothing', 'food'];
        return validCategories.includes(value) || 'Invalid category';
      }
    }
  },
  {
    name: 'inStock',
    defaultValue: true
  }
];

// Create configuration
const config: DataSourceConfig<Product> = {
  url: '/api/products',
  fields,
  cache: {
    enabled: true,
    ttl: 300000
  },
  retry: {
    maxRetries: 3,
    retryDelay: 1000
  },
  pageSize: 20
};

// Create DataSource
const products = new DataSource<Product>(config);

// Type-safe queries
const query: QueryOptions<Product> = {
  filter: (p) => p.inStock && p.price < 100,
  sort: 'name',
  limit: 10
};

await products.fetch(query);

// Type-safe operations
const newProduct: Partial<Product> = {
  name: 'Laptop',
  price: 999,
  category: 'electronics'
};

const created = await products.create(newProduct);
// created is of type Product

Using Signals with Types

typescript
import { createSignal } from 'bindra';
import type { Signal } from 'bindra';

interface AppState {
  user: User | null;
  loading: boolean;
  error: string | null;
}

const state: Signal<AppState> = createSignal({
  user: null,
  loading: false,
  error: null
});

// Subscribe with type safety
state.subscribe((value: AppState) => {
  if (value.loading) {
    showSpinner();
  } else {
    hideSpinner();
  }
  
  if (value.error) {
    showError(value.error);
  }
});

// Update state
state.set({
  user: { id: 1, name: 'Alice', email: 'alice@example.com' },
  loading: false,
  error: null
});

Generic DataSource Utility

typescript
import { DataSource } from 'bindra';
import type { DataSourceConfig } from 'bindra';

// Create a factory function with proper types
function createApiDataSource<T extends Record<string, any>>(
  endpoint: string,
  config?: Partial<DataSourceConfig<T>>
): DataSource<T> {
  return new DataSource<T>({
    url: `https://api.example.com${endpoint}`,
    cache: { enabled: true, ttl: 300000 },
    retry: { maxRetries: 3 },
    ...config
  });
}

// Use with different types
interface User {
  id: number;
  name: string;
  email: string;
}

interface Post {
  id: number;
  title: string;
  content: string;
  authorId: number;
}

const users = createApiDataSource<User>('/users');
const posts = createApiDataSource<Post>('/posts', {
  pageSize: 50
});

// All operations are type-safe
const newUser = await users.create({
  name: 'Alice',
  email: 'alice@example.com'
  // TypeScript error if we forget required fields or add invalid fields
});

Type Guards

Checking Record Types

typescript
// Type guard for records
function isUser(record: any): record is User {
  return (
    typeof record === 'object' &&
    typeof record.id === 'number' &&
    typeof record.name === 'string' &&
    typeof record.email === 'string'
  );
}

// Usage
const data = await fetch('/api/user/1').then(r => r.json());

if (isUser(data)) {
  // data is now typed as User
  console.log(data.email);
}

Checking Validation Errors

typescript
import { isBindraError, isValidationError } from 'bindra';

try {
  await dataSource.create(newRecord);
} catch (error) {
  if (isValidationError(error)) {
    // error.details contains field errors
    console.log('Validation failed:', error.details);
  } else if (isBindraError(error)) {
    console.log('Bindra error:', error.message);
  } else {
    console.log('Unknown error:', error);
  }
}

Best Practices

1. Define Interfaces for All Models

typescript
// ✅ Good - Clear type definitions
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

const users = new DataSource<User>({ url: '/api/users' });

// ❌ Bad - No type safety
const users = new DataSource({ url: '/api/users' });

2. Use Partial<T> for Creates/Updates

typescript
// ✅ Good - Allows partial data
async function updateUser(id: number, changes: Partial<User>) {
  return await users.update(id, changes);
}

updateUser(1, { name: 'Bob' }); // OK

// ❌ Bad - Requires all fields
async function updateUser(id: number, changes: User) {
  return await users.update(id, changes);
}

updateUser(1, { name: 'Bob' }); // TypeScript error

3. Export and Reuse Type Definitions

typescript
// types/user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export type UserCreate = Omit<User, 'id'>;
export type UserUpdate = Partial<UserCreate>;

// services/userService.ts
import type { User, UserCreate, UserUpdate } from '../types/user';

export async function createUser(data: UserCreate): Promise<User> {
  return await users.create(data);
}

export async function updateUser(id: number, data: UserUpdate): Promise<User> {
  return await users.update(id, data);
}

4. Leverage Type Inference

typescript
// ✅ Good - Type inferred from variable
const fields: FieldConfig<User>[] = [
  {
    name: 'email', // Type-safe: must be keyof User
    validation: { type: 'email' }
  }
];

// ✅ Good - Type inferred from generic
const validateEmail = async (email: string): Promise<boolean | string> => {
  // Custom validation logic
  return true;
};

const emailField: FieldConfig<User> = {
  name: 'email',
  validation: {
    custom: validateEmail // Type matches automatically
  }
};


← Back to API Reference

Released under the MIT License.