Backups API - Dart SDK Documentation
Overview
The Backups API provides endpoints for managing application data backups. You can create backups, upload existing backup files, download backups, delete backups, and restore the application from a backup.
Key Features:
- List all available backup files
- Create new backups with custom names or auto-generated names
- Upload existing backup ZIP files
- Download backup files (requires file token)
- Delete backup files
- Restore the application from a backup (restarts the app)
Backend Endpoints:
GET /api/backups- List backupsPOST /api/backups- Create backupPOST /api/backups/upload- Upload backupGET /api/backups/{key}- Download backupDELETE /api/backups/{key}- Delete backupPOST /api/backups/{key}/restore- Restore backup
Note: All Backups API operations require superuser authentication (except download which requires a superuser file token).
Authentication
All Backups API operations require superuser authentication:
```dart import ‘package:bosbase/bosbase.dart’;
final pb = Bosbase(‘http://127.0.0.1:8090’);
// Authenticate as superuser await pb.collection(’_superusers’).authWithPassword(‘admin@example.com’, ‘password’); ```
Downloading backups requires a superuser file token (obtained via `pb.files.getToken()`), but does not require the Authorization header.
Backup File Structure
Each backup file contains:
- `key`: The filename/key of the backup file (String)
- `size`: File size in bytes (int)
- `modified`: ISO 8601 timestamp of when the backup was last modified (String)
```dart class BackupFileInfo { String key; int size; String modified; } ```
List Backups
Returns a list of all available backup files with their metadata.
Basic Usage
```dart // Get all backups final backups = await pb.backups.getFullList();
print(backups); // [ // BackupFileInfo( // key: “pb_backup_20230519162514.zip”, // modified: “2023-05-19T16:25:57.542Z”, // size: 251316185 // ), // BackupFileInfo( // key: “pb_backup_20230518162514.zip”, // modified: “2023-05-18T16:25:57.542Z”, // size: 251314010 // ) // ] ```
Working with Backup Lists
```dart // Sort backups by modification date (newest first) final backups = await pb.backups.getFullList(); backups.sort((a, b) => DateTime.parse(b.modified).compareTo(DateTime.parse(a.modified)));
// Find the most recent backup final mostRecent = backups.isNotEmpty ? backups.first : null;
// Filter backups by size (larger than 100MB) final largeBackups = backups.where((backup) => backup.size > 100 * 1024 * 1024).toList();
// Get total storage used by backups
final totalSize = backups.fold
Create Backup
Creates a new backup of the application data. The backup process is asynchronous and may take some time depending on the size of your data.
Basic Usage
```dart // Create backup with custom name await pb.backups.create(‘my_backup_2024.zip’);
// Create backup with auto-generated name (pass empty string or let backend generate) await pb.backups.create(’’); ```
Backup Name Format
Backup names must follow the format: `[a-z0-9_-].zip`
- Only lowercase letters, numbers, underscores, and hyphens
- Must end with `.zip`
- Maximum length: 150 characters
- Must be unique (no existing backup with the same name)
Examples
```dart
// Create a named backup
Future
// Create backup with timestamp
Future
Important Notes
- Asynchronous Process: Backup creation happens in the background. The API returns immediately (204 No Content).
- Concurrent Operations: Only one backup or restore operation can run at a time. If another operation is in progress, you’ll receive a 400 error.
- Storage: Backups are stored in the configured backup filesystem (local or S3).
- S3 Consistency: For S3 storage, the backup file may not be immediately available after creation due to eventual consistency.
Upload Backup
Uploads an existing backup ZIP file to the server. This is useful for restoring backups created elsewhere or for importing backups.
Basic Usage
```dart import ‘package:http/http.dart’ as http; import ‘dart:io’;
// Upload from a file final file = File(‘backup.zip’); final multipartFile = await http.MultipartFile.fromPath(‘file’, file.path); await pb.backups.upload(multipartFile);
// Upload from bytes final bytes = await File(‘backup.zip’).readAsBytes(); final multipartFile = http.MultipartFile.fromBytes(‘file’, bytes, filename: ‘backup.zip’); await pb.backups.upload(multipartFile); ```
File Requirements
- MIME Type: Must be `application/zip`
- Format: Must be a valid ZIP archive
- Name: Must be unique (no existing backup with the same name)
- Validation: The file will be validated before upload
Examples
```dart
// Upload backup from file
Future
final multipartFile = await http.MultipartFile.fromPath('file', file.path);
await pb.backups.upload(multipartFile);
print('Backup uploaded successfully');
} on ClientException catch (e) { if (e.statusCode == 400) { print(‘Invalid file or file already exists’); } else { print(‘Upload failed: $e’); } } }
// Upload backup from URL (e.g., downloading from another server)
Future
final filename = url.split(’/’).last.isEmpty ? ‘backup.zip’ : url.split(’/’).last; final multipartFile = http.MultipartFile.fromBytes(‘file’, response.bodyBytes, filename: filename); await pb.backups.upload(multipartFile); } ```
Download Backup
Downloads a backup file. Requires a superuser file token for authentication.
Basic Usage
```dart import ‘dart:io’; import ‘package:http/http.dart’ as http;
// Get file token final token = await pb.files.getToken();
// Build download URL final url = pb.backups.getDownloadURL(token, ‘pb_backup_20230519162514.zip’);
// Download the file final response = await http.get(url); if (response.statusCode == 200) { final file = File(‘pb_backup_20230519162514.zip’); await file.writeAsBytes(response.bodyBytes); print(‘Backup downloaded successfully’); } ```
Download URL Structure
The download URL format is: ``` /api/backups/{key}?token={fileToken} ```
Examples
```dart
// Download backup function
Future
// Build download URL
final url = pb.backups.getDownloadURL(token, backupKey);
// Download the file
final response = await http.get(url);
if (response.statusCode == 200) {
final file = File(savePath);
await file.writeAsBytes(response.bodyBytes);
print('Backup downloaded to: $savePath');
} else {
print('Failed to download backup: ${response.statusCode}');
}
} catch (e) { print(‘Failed to download backup: $e’); } }
// Download and save backup with custom name
Future
final response = await http.get(url); if (response.statusCode == 200) { final file = File(saveAs); await file.writeAsBytes(response.bodyBytes); } } ```
Delete Backup
Deletes a backup file from the server.
Basic Usage
```dart await pb.backups.delete(‘pb_backup_20230519162514.zip’); ```
Important Notes
- Active Backups: Cannot delete a backup that is currently being created or restored
- No Undo: Deletion is permanent
- File System: The file will be removed from the backup filesystem
Examples
```dart
// Delete backup with confirmation check
Future
// Delete old backups (older than 30 days)
Future
final oldBackups = backups.where((backup) { final modified = DateTime.parse(backup.modified); return modified.isBefore(thirtyDaysAgo); }).toList();
for (final backup in oldBackups) { try { await pb.backups.delete(backup.key); print(‘Deleted old backup: ${backup.key}’); } catch (e) { print(‘Failed to delete ${backup.key}: $e’); } } } ```
Restore Backup
Restores the application from a backup file. This operation will restart the application.
Basic Usage
```dart await pb.backups.restore(‘pb_backup_20230519162514.zip’); ```
Important Warnings
⚠️ CRITICAL: Restoring a backup will:
- Replace all current application data with data from the backup
- Restart the application process
- Any unsaved changes will be lost
- The application will be unavailable during the restore process
Prerequisites
- Disk Space: Recommended to have at least 2x the backup size in free disk space
- UNIX Systems: Restore is primarily supported on UNIX-based systems (Linux, macOS)
- No Concurrent Operations: Cannot restore if another backup or restore is in progress
- Backup Existence: The backup file must exist on the server
Restore Process
The restore process performs the following steps:
- Downloads the backup file to a temporary location
- Extracts the backup to a temporary directory
- Moves current `pb_data` content to a temporary location (to be deleted on next app start)
- Moves extracted backup content to `pb_data`
- Restarts the application
Examples
```dart
// Restore backup with confirmation check
Future
Complete Examples
Example 1: Backup Manager Class
```dart class BackupManager { final Bosbase pb;
BackupManager(this.pb);
Future<List
Future
Future
Future
Future
Future
final toDelete = backups.where((b) => DateTime.parse(b.modified).isBefore(cutoff)).toList();
int deleted = 0;
for (final backup in toDelete) {
try {
await delete(backup.key);
print('Deleted: ${backup.key}');
deleted++;
} catch (e) {
print('Failed to delete ${backup.key}: $e');
}
}
return deleted;
}
String generateTimestampedName() { final timestamp = DateTime.now().toIso8601String() .replaceAll(’:’, ‘-’) .replaceAll(’.’, ‘-’) .substring(0, 19); return ‘backup$timestamp.zip’; } }
// Usage final manager = BackupManager(pb); final backups = await manager.list(); await manager.create(); ```
Example 2: Automated Backup Strategy
```dart enum BackupStrategy { daily, weekly, monthly }
class AutomatedBackup { final Bosbase pb; final BackupStrategy strategy; final int maxBackups;
AutomatedBackup(this.pb, {this.strategy = BackupStrategy.daily, this.maxBackups = 7});
Future
await _cleanupOldBackups();
} catch (e) {
print('Backup creation failed: $e');
}
}
String _generateBackupName() { final now = DateTime.now(); final dateStr = ‘${now.year}-${now.month.toString().padLeft(2, ‘0’)}-${now.day.toString().padLeft(2, ‘0’)}’;
switch (strategy) {
case BackupStrategy.daily:
return 'daily_$dateStr.zip';
case BackupStrategy.weekly:
final week = (now.day / 7).ceil();
return 'weekly_${now.year}_W$week.zip';
case BackupStrategy.monthly:
return 'monthly_${now.year}_${now.month.toString().padLeft(2, '0')}.zip';
}
}
Future
if (sorted.length > maxBackups) {
final toDelete = sorted.skip(maxBackups);
for (final backup in toDelete) {
try {
await pb.backups.delete(backup.key);
print('Cleaned up old backup: ${backup.key}');
} catch (e) {
print('Failed to delete ${backup.key}: $e');
}
}
}
} }
// Setup daily automated backups final autoBackup = AutomatedBackup(pb, strategy: BackupStrategy.daily);
// Run backup (could be called from a timer or scheduler) Timer.periodic(Duration(days: 1), (_) { autoBackup.createScheduledBackup(); }); ```
Example 3: Backup Migration Tool
```dart import ‘package:http/http.dart’ as http;
class BackupMigrator { final Bosbase sourcePb; final Bosbase targetPb;
BackupMigrator(this.sourcePb, this.targetPb);
Future
// Step 1: Download from source
print('Downloading from source...');
final sourceToken = await sourcePb.files.getToken();
final downloadUrl = sourcePb.backups.getDownloadURL(sourceToken, backupKey);
final response = await http.get(downloadUrl);
if (response.statusCode != 200) {
throw Exception('Failed to download backup from source');
}
// Step 2: Create multipart file
final multipartFile = http.MultipartFile.fromBytes(
'file',
response.bodyBytes,
filename: backupKey,
);
// Step 3: Upload to target
print('Uploading to target...');
await targetPb.backups.upload(multipartFile);
print('Migration completed');
}
Future
for (final backup in backups) {
try {
await migrateBackup(backup.key);
print('✓ Migrated: ${backup.key}');
} catch (e) {
print('✗ Failed to migrate ${backup.key}: $e');
}
}
} }
// Usage final migrator = BackupMigrator(sourcePb, targetPb); await migrator.migrateAllBackups(); ```
Example 4: Backup Health Check
```dart
Future
if (backups.isEmpty) { print(’⚠️ No backups found!’); return false; }
// Check for recent backup (within last 7 days) final sevenDaysAgo = DateTime.now().subtract(Duration(days: 7));
final recentBackups = backups.where((b) { final modified = DateTime.parse(b.modified); return modified.isAfter(sevenDaysAgo); }).toList();
if (recentBackups.isEmpty) { print(’⚠️ No backups found in the last 7 days’); } else { print(’✓ Found ${recentBackups.length} recent backup(s)’); }
// Check total storage
final totalSize = backups.fold
// Check largest backup if (backups.isNotEmpty) { final largest = backups.reduce((max, b) => b.size > max.size ? b : max); final largestSizeMB = (largest.size / 1024 / 1024).toStringAsFixed(2); print(‘Largest backup: ${largest.key} ($largestSizeMB MB)’); }
return true; } ```
Error Handling
```dart
// Handle common backup errors
Future
case 401:
print('Not authenticated');
break;
case 403:
print('Not a superuser');
break;
case 404:
print('Backup not found');
break;
default:
print('Unexpected error: $e');
}
rethrow;
} } ```
Best Practices
- Regular Backups: Create backups regularly (daily, weekly, or based on your needs)
- Naming Convention: Use clear, consistent naming (e.g., `backup_YYYY-MM-DD.zip`)
- Backup Rotation: Implement cleanup to remove old backups and prevent storage issues
- Test Restores: Periodically test restoring backups to ensure they work
- Off-site Storage: Download and store backups in a separate location
- Pre-Restore Backup: Always create a backup before restoring (if possible)
- Monitor Storage: Monitor backup storage usage to prevent disk space issues
- Documentation: Document your backup and restore procedures
- Automation: Use cron jobs or schedulers for automated backups
- Verification: Verify backup integrity after creation/download
Limitations
- Superuser Only: All operations require superuser authentication
- Concurrent Operations: Only one backup or restore can run at a time
- Restore Restart: Restoring a backup restarts the application
- UNIX Systems: Restore primarily works on UNIX-based systems
- Disk Space: Restore requires significant free disk space (2x backup size recommended)
- S3 Consistency: S3 backups may not be immediately available after creation
- Active Backups: Cannot delete backups that are currently being created or restored
Related Documentation
- File API - File handling and tokens
- Crons API - Automated backup scheduling
- Collection API - Collection management