Skip to content

Import Export Overview

Masked-Kunsiquat edited this page Dec 25, 2025 · 2 revisions

Import/Export Module Overview

The Import/Export module provides comprehensive data portability for CrewSplit, enabling users to backup their data, share trips with others, and migrate between devices.

Architecture

The module follows CrewSplit's modular architecture with clear separation of concerns:

src/modules/import-export/
├── core/              # Core types, validation, entity registry
│   ├── types.ts       # TypeScript interfaces and types
│   ├── validators.ts  # JSON schema validation
│   ├── registry.ts    # Entity registration system
│   └── errors.ts      # Custom error types
├── entities/          # Entity-specific import/export logic
│   ├── trip-entity.ts
│   ├── participant-entity.ts
│   ├── expense-entity.ts
│   ├── expense-split-entity.ts
│   ├── expense-category-entity.ts
│   ├── fx-rate-entity.ts
│   └── settlement-entity.ts
├── service/           # High-level orchestration
│   ├── ExportService.ts
│   └── ImportService.ts
├── hooks/             # React hooks for UI integration
│   ├── use-export.ts
│   └── use-import.ts
└── screens/           # UI components
    ├── ImportExportScreen.tsx
    └── SettingsExportSection.tsx

Key Features

Data Export

  • Single Trip Export: Export a specific trip with all related data (participants, expenses, splits, settlements)
  • Full Database Export: Export entire database including all trips, categories, and FX rates
  • Configurable Options:
    • Include/exclude sample data
    • Include/exclude archived data
  • Native Sharing: Uses platform share dialog for cross-app compatibility

Data Import

  • File Validation: Comprehensive JSON structure and schema validation
  • Conflict Resolution: Three strategies for handling duplicate IDs:
    • skip: Preserve existing data (default, safest)
    • replace: Overwrite existing data (data loss risk)
    • generate_new_ids: Create new UUIDs (Phase 2 - not yet implemented)
  • Foreign Key Validation: Ensures referential integrity
  • Atomic Transactions: All-or-nothing imports with automatic rollback on error
  • Dry Run Mode: Preview imports without writing to database

File Format

Export files use JSON format with versioning for future compatibility:

{
  "version": "1.0.0",
  "exportedAt": "2025-01-15T10:30:00Z",
  "appVersion": "1.1.0",
  "scope": "single_trip" | "full_database" | "global_data",
  "metadata": {
    "tripId": "trip-uuid",
    "tripName": "Summer Vacation",
    "exportedBy": "device-id"
  },
  "data": {
    "trips": [...],
    "participants": [...],
    "expenses": [...],
    "expenseSplits": [...],
    "expenseCategories": [...],
    "fxRates": [...],
    "settlements": [...]
  }
}

Entity System

Each database table implements the ExportableEntity interface:

interface ExportableEntity<T> {
  name: string;
  dependencies: string[]; // Export order (parents before children)
  scope: "global" | "trip" | "both";

  export(context: ExportContext): Promise<T[]>;
  import(records: T[], context: ImportContext): Promise<ImportResult>;
  validate(records: T[]): ValidationError[];
  transform?(records: T[], fromVersion: string): Promise<T[]>;
}

Dependency Order

Entities are imported in dependency order to ensure referential integrity:

  1. trips (no dependencies)
  2. expenseCategories (no dependencies)
  3. fxRates (no dependencies)
  4. participants (depends on trips)
  5. expenses (depends on trips, participants, expenseCategories)
  6. expenseSplits (depends on expenses, participants)
  7. settlements (depends on trips, participants, expenseSplits)

Transaction Handling

The import system uses database transactions for atomicity:

// ImportService creates transaction
await db.transaction(async (tx) => {
  const context: ImportContext = {
    conflictResolution,
    validateForeignKeys: true,
    dryRun: false,
    tx, // Transaction passed to all entities
  };

  for (const entity of entities) {
    await entity.import(records, context);
  }
});

Each entity uses the transaction for all database operations:

const dbClient = context.tx ?? db; // Use transaction if provided
await dbClient.insert(table).values(record);

This ensures:

  • All-or-nothing: Either all entities import successfully or none do
  • Automatic rollback: Any error triggers complete rollback
  • Data integrity: No partial imports that could corrupt the database

Error Handling

The module provides structured error handling:

Error Types

  • InvalidExportFileError: Malformed JSON or missing required fields
  • UnsupportedVersionError: Export file version not supported
  • ValidationException: Data validation failed before import
  • NoRateAvailableError: Missing FX rate for currency conversion

Error Recovery

  • Import errors are collected and returned in ImportResult
  • UI displays detailed error summaries
  • Users can retry with different conflict resolution strategies
  • Validation runs before database writes to fail fast

Performance Considerations

  • Streaming: Large exports use chunked file writing
  • Batching: Imports process records in batches for memory efficiency
  • Indexes: Database queries use indexed columns (ID, tripId)
  • Deferred FK checks: Foreign key validation batched for efficiency

Security & Privacy

  • No cloud storage: All imports/exports are local-only
  • No telemetry: Export files contain no tracking data
  • User control: Users choose what to export and when
  • Data minimization: Export only requested scope (trip vs full DB)

Testing Strategy

See __tests__/ directories for comprehensive test coverage:

  • Unit tests for each entity's import/export logic
  • Integration tests for full export → import round-trips
  • Edge cases: empty data, missing FKs, duplicate IDs
  • Error scenarios: malformed JSON, version mismatches
  • Transaction rollback verification

Related Documentation

Clone this wiki locally