-
Notifications
You must be signed in to change notification settings - Fork 0
Import Export Overview
Masked-Kunsiquat edited this page Dec 25, 2025
·
2 revisions
The Import/Export module provides comprehensive data portability for CrewSplit, enabling users to backup their data, share trips with others, and migrate between devices.
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
- 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
- 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
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": [...]
}
}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[]>;
}Entities are imported in dependency order to ensure referential integrity:
- trips (no dependencies)
- expenseCategories (no dependencies)
- fxRates (no dependencies)
- participants (depends on trips)
- expenses (depends on trips, participants, expenseCategories)
- expenseSplits (depends on expenses, participants)
- settlements (depends on trips, participants, expenseSplits)
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
The module provides structured error handling:
-
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
- 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
- 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
- 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)
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
- Import Export Usage Guide - Developer integration guide
- Offline and Backup - End-user documentation
- Developer Guidelines - General development practices
- Architecture & Agents - System architecture overview