Skip to content

Migration Guide: Upgrading to Bindra v2.0

This guide will help you upgrade your application from Bindra v1.x to v2.0.

Overview

Bindra v2.0 introduces several breaking changes focused on improved type safety, better error handling, and enhanced features. While there are breaking changes, the migration path is straightforward.


Table of Contents


What's New in v2.0

Major Additions

Generic Type Support - Full TypeScript generics for type-safe operations
Custom Error Types - ValidationError, NetworkError, NotFoundError
Validation System - Comprehensive built-in validation
Batch Operations - Efficient bulk CRUD operations
Optimistic Updates - Instant UI feedback with auto-rollback
Caching System - TTL-based cache with manual invalidation
Pagination - Offset and cursor-based pagination
WebSocket Support - Real-time bidirectional communication
Security Features - XSS sanitization and CSRF tokens
Performance Utilities - debounce, throttle, debounceWithMaxWait

Improvements

  • Enhanced TypeScript strict mode compliance
  • Better error messages with stack traces
  • Improved JSDoc documentation
  • Async initialization for Container
  • Auto-reconnect for WebSocket connections

Breaking Changes

1. TypeScript Generic Requirement

Before (v1.x):

typescript
const ds = new DataSource({
  data: [{ id: 1, name: 'Alice' }]
});

After (v2.0):

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

const ds = new DataSource<User>({
  data: [{ id: 1, name: 'Alice' }]
});

Migration: Add explicit type parameters to all DataSource instances.


2. Error Types Changed

Before (v1.x):

typescript
try {
  await ds.create(record);
} catch (error) {
  // Generic Error
  console.error(error.message);
}

After (v2.0):

typescript
import { ValidationError, NetworkError, NotFoundError } from 'bindra';

try {
  await ds.create(record);
} catch (error) {
  if (error instanceof ValidationError) {
    // Handle validation errors
    console.error('Validation failed:', error.errors);
  } else if (error instanceof NetworkError) {
    // Handle network errors
    console.error('Network error:', error.statusCode);
  } else if (error instanceof NotFoundError) {
    // Handle not found errors
    console.error('Record not found:', error.id);
  }
}

Migration: Update error handling to check for specific error types.


3. Container Initialization is Now Async

Before (v1.x):

typescript
const container = new Container();
container.register('users', userConfig);
const ds = container.get('users');

After (v2.0):

typescript
const container = new Container();
await container.register('users', userConfig);
const ds = await container.get('users');

Migration: Add await when calling register() and get().


4. Query Options Structure Changed

Before (v1.x):

typescript
await ds.query({
  where: { active: true },
  orderBy: 'name',
  take: 10
});

After (v2.0):

typescript
await ds.fetch({
  filter: { active: true },
  sort: { field: 'name', order: 'asc' },
  limit: 10
});

Migration: Update query option property names:

  • wherefilter
  • orderBysort (now an object)
  • takelimit

5. Event Names Changed

Before (v1.x):

typescript
ds.on('fetch', () => {});
ds.on('create', () => {});

After (v2.0):

typescript
ds.on('beforeFetch', () => {});
ds.on('afterFetch', () => {});
ds.on('beforeCreate', () => {});
ds.on('afterCreate', () => {});

Migration: Add before or after prefix to all event names.


Migration Steps

Step 1: Update Package

bash
npm install bindra@2.0.0
# or
pnpm update bindra
# or
yarn upgrade bindra

Step 2: Add Type Definitions

Define TypeScript interfaces for all your data models:

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

export interface Product {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
}

Step 3: Update DataSource Instances

Add generic type parameters:

typescript
// Before
const users = new DataSource({ url: '/api/users' });
const products = new DataSource({ data: [] });

// After
const users = new DataSource<User>({ url: '/api/users' });
const products = new DataSource<Product>({ data: [] });

Step 4: Update Error Handling

Replace generic error handling with specific error types:

typescript
import { 
  ValidationError, 
  NetworkError, 
  NotFoundError 
} from 'bindra';

try {
  const user = await users.create(newUser);
} catch (error) {
  if (error instanceof ValidationError) {
    // Show validation errors to user
    showErrors(error.errors);
  } else if (error instanceof NetworkError) {
    // Show network error message
    showNetworkError(error.statusCode);
  } else if (error instanceof NotFoundError) {
    // Handle not found
    showNotFound();
  }
}

Step 5: Update Event Listeners

Add before/after prefixes:

typescript
// Before
users.on('fetch', handleFetch);
users.on('create', handleCreate);
users.on('update', handleUpdate);

// After
users.on('afterFetch', handleFetch);
users.on('afterCreate', handleCreate);
users.on('afterUpdate', handleUpdate);

Step 6: Update Container Usage

Make Container operations async:

typescript
// Before
const container = new Container();
container.register('users', config);
const users = container.get('users');

// After
const container = new Container();
await container.register('users', config);
const users = await container.get('users');

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

Step 7: Update Query Calls

Update query option names:

typescript
// Before
await users.query({
  where: { active: true },
  orderBy: 'name',
  take: 20
});

// After
await users.fetch({
  filter: { active: true },
  sort: { field: 'name', order: 'asc' },
  limit: 20
});

New Features

Validation System

Add validation rules to your DataSource:

typescript
const users = new DataSource<User>({
  url: '/api/users',
  validation: {
    rules: {
      name: [
        { type: 'required', message: 'Name is required' },
        { type: 'minLength', value: 2, message: 'Name too short' }
      ],
      email: [
        { type: 'required' },
        { type: 'email', message: 'Invalid email' }
      ],
      age: [
        { type: 'min', value: 18, message: 'Must be 18+' }
      ]
    }
  }
});

Batch Operations

Perform bulk operations efficiently:

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

// Update multiple records
await users.updateBatch([
  { id: 1, changes: { name: 'Alice Smith' } },
  { id: 2, changes: { name: 'Bob Jones' } }
]);

// Delete multiple records
await users.deleteBatch([1, 2, 3]);

Optimistic Updates

Enable instant UI feedback:

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

// Update happens instantly in UI
// Automatically rolled back on error
await users.update(1, { name: 'New Name' });

Caching

Add TTL-based caching:

typescript
const users = new DataSource<User>({
  url: '/api/users',
  cache: {
    enabled: true,
    ttl: 300000 // 5 minutes
  }
});

// First call hits API
await users.fetch();

// Second call uses cache (within 5 min)
await users.fetch();

// Manual cache invalidation
users.invalidateCache();

Pagination

Use built-in pagination:

typescript
const users = new DataSource<User>({
  url: '/api/users',
  pagination: {
    enabled: true,
    pageSize: 20,
    strategy: 'offset' // or 'cursor'
  }
});

// Load first page
await users.fetch();

// Load more
await users.fetchMore();

// Check state
console.log(users.currentPage);  // Current page number
console.log(users.hasMore);      // More data available?

WebSocket / Real-time

Add real-time updates:

typescript
const chat = new DataSource<Message>({
  url: '/api/messages',
  realtime: {
    enabled: true,
    url: 'wss://api.example.com/ws',
    events: ['message.created', 'message.updated']
  }
});

// Automatically receives real-time updates
// Auto-reconnects on disconnect

Security Features

Enable XSS protection and CSRF tokens:

typescript
const users = new DataSource<User>({
  url: '/api/users',
  security: {
    xssProtection: true,
    csrfToken: true,
    sanitizeFields: ['name', 'bio', 'comment']
  }
});

Performance Utilities

Use debounce and throttle:

typescript
import { debounce, throttle, debounceWithMaxWait } from 'bindra';

// Debounce search input
const handleSearch = debounce(async (query: string) => {
  await users.fetch({ filter: { name: query } });
}, 300);

// Throttle scroll handler
const handleScroll = throttle(() => {
  if (isAtBottom()) {
    users.fetchMore();
  }
}, 200);

// Debounce with max wait
const saveForm = debounceWithMaxWait(
  async (data) => await users.update(1, data),
  1000,
  5000
);

Deprecations

The following features are deprecated and will be removed in v3.0:

query() method

Deprecated: Use fetch() instead
Removal: v3.0

typescript
// Deprecated
await users.query({ where: { active: true } });

// Use this
await users.fetch({ filter: { active: true } });

Generic event names

Deprecated: Use before* and after* events
Removal: v3.0

typescript
// Deprecated
users.on('create', handleCreate);

// Use this
users.on('afterCreate', handleCreate);

Testing Your Migration

1. TypeScript Errors

Run TypeScript compiler to catch type errors:

bash
npx tsc --noEmit

2. Runtime Testing

Test all CRUD operations:

typescript
// Create
const created = await ds.create(record);
console.assert(created.id, 'Create failed');

// Read
await ds.fetch();
console.assert(ds.data?.length, 'Fetch failed');

// Update
const updated = await ds.update(1, { name: 'New' });
console.assert(updated.name === 'New', 'Update failed');

// Delete
await ds.delete(1);
console.assert(!ds.data?.find(r => r.id === 1), 'Delete failed');

3. Error Handling

Test that errors are properly typed:

typescript
try {
  await ds.create({});
} catch (error) {
  console.assert(error instanceof ValidationError, 'Wrong error type');
}

Need Help?


Happy migrating! 🚀

Released under the MIT License.