Asynchronous, non-blocking SQLite3 bindings for Node.js
—
Database backup and restoration functionality with step-by-step control and progress monitoring. The Backup class extends EventEmitter and provides comprehensive backup operations for SQLite databases.
Creates a backup instance for database backup operations.
/**
* Creates a database backup (simple form) - backs up main database to file
* @param filename - Destination filename for backup
* @param callback - Optional callback called when backup is initialized
* @returns Backup instance
*/
backup(filename: string, callback?: (err: Error | null) => void): Backup;
/**
* Creates a database backup (advanced form) - full control over source/destination
* @param filename - Source or destination filename
* @param destName - Destination database name (usually 'main')
* @param sourceName - Source database name (usually 'main')
* @param filenameIsDest - Whether filename parameter is destination (true) or source (false)
* @param callback - Optional callback called when backup is initialized
* @returns Backup instance
*/
backup(
filename: string,
destName: string,
sourceName: string,
filenameIsDest: boolean,
callback?: (err: Error | null) => void
): Backup;Usage Examples:
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('source.db');
// Simple backup - backup current database to file
const backup = db.backup('backup.db', (err) => {
if (err) {
console.error('Backup initialization failed:', err.message);
} else {
console.log('Backup initialized successfully');
}
});
// Advanced backup - restore from file to current database
const restoreBackup = db.backup('backup.db', 'main', 'main', false, (err) => {
if (err) {
console.error('Restore initialization failed:', err.message);
}
});
// Backup to a different database name
const attachedBackup = db.backup('backup.db', 'backup_db', 'main', true);Advances the backup process by copying a specified number of pages.
/**
* Steps the backup process forward by copying pages
* @param pages - Number of pages to copy in this step (-1 for all remaining)
* @param callback - Optional callback called when step completes
* @returns Backup instance for chaining
*/
step(pages: number, callback?: (err: Error | null) => void): this;Usage Examples:
const backup = db.backup('backup.db');
// Copy 5 pages at a time
backup.step(5, (err) => {
if (err) {
console.error('Backup step failed:', err.message);
} else {
console.log(`Backup progress: ${backup.remaining} pages remaining`);
if (!backup.completed && !backup.failed) {
// Continue with next step
backup.step(5);
}
}
});
// Copy all remaining pages at once
backup.step(-1, (err) => {
if (err) {
console.error('Backup failed:', err.message);
} else {
console.log('Backup completed successfully');
}
});
// Step-by-step with progress monitoring
function performBackupWithProgress() {
const backup = db.backup('backup.db');
const totalPages = backup.pageCount;
function stepBackup() {
backup.step(1, (err) => {
if (err) {
console.error('Backup error:', err.message);
return;
}
const progress = ((totalPages - backup.remaining) / totalPages) * 100;
console.log(`Backup progress: ${progress.toFixed(1)}%`);
if (backup.completed) {
console.log('Backup completed successfully');
backup.finish();
} else if (backup.failed) {
console.log('Backup failed');
backup.finish();
} else {
// Continue with next step
setTimeout(stepBackup, 10); // Small delay between steps
}
});
}
stepBackup();
}Finishes the backup operation and releases resources.
/**
* Finishes the backup operation and releases resources
* @param callback - Optional callback called when backup is finished
* @returns Backup instance for chaining
*/
finish(callback?: (err: Error | null) => void): this;Usage Examples:
const backup = db.backup('backup.db');
// Perform complete backup
backup.step(-1, (err) => {
if (err) {
console.error('Backup failed:', err.message);
} else {
console.log('Backup data copied successfully');
}
// Always finish to clean up resources
backup.finish((err) => {
if (err) {
console.error('Backup finish failed:', err.message);
} else {
console.log('Backup operation completed and resources cleaned up');
}
});
});Read-only properties that provide backup status and progress information.
/**
* Number of pages remaining to be backed up
*/
readonly remaining: number;
/**
* Total number of pages in the source database
*/
readonly pageCount: number;
/**
* Whether the backup has completed successfully
*/
readonly completed: boolean;
/**
* Whether the backup has failed
*/
readonly failed: boolean;
/**
* Whether the backup is currently idle (no operation in progress or queued)
*/
readonly idle: boolean;
/**
* Array of SQLite error codes that should trigger retry attempts
* Default: [BUSY, LOCKED]
*/
retryErrors: number[];Usage Examples:
const backup = db.backup('backup.db');
// Monitor backup progress
function checkProgress() {
console.log(`Pages: ${backup.remaining}/${backup.pageCount} remaining`);
console.log(`Progress: ${((backup.pageCount - backup.remaining) / backup.pageCount * 100).toFixed(1)}%`);
console.log(`Completed: ${backup.completed}`);
console.log(`Failed: ${backup.failed}`);
}
// Configure retry behavior
backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED, sqlite3.INTERRUPT];
// Step through backup with monitoring
backup.step(10, (err) => {
checkProgress();
if (!backup.completed && !backup.failed) {
// Continue backup
backup.step(10);
} else {
backup.finish();
}
});const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('source.db');
function createBackup(sourceDb, backupFile, callback) {
const backup = sourceDb.backup(backupFile, (err) => {
if (err) {
return callback(err);
}
// Perform complete backup
backup.step(-1, (err) => {
if (err) {
backup.finish();
return callback(err);
}
if (backup.completed) {
backup.finish((err) => {
if (err) {
return callback(err);
}
callback(null, 'Backup completed successfully');
});
} else {
backup.finish();
callback(new Error('Backup did not complete'));
}
});
});
}
// Usage
createBackup(db, 'my-backup.db', (err, message) => {
if (err) {
console.error('Backup failed:', err.message);
} else {
console.log(message);
}
db.close();
});function createProgressiveBackup(sourceDb, backupFile, progressCallback, completeCallback) {
const backup = sourceDb.backup(backupFile, (err) => {
if (err) {
return completeCallback(err);
}
const totalPages = backup.pageCount;
console.log(`Starting backup of ${totalPages} pages`);
function stepWithProgress() {
// Copy 100 pages at a time
backup.step(100, (err) => {
if (err) {
backup.finish();
return completeCallback(err);
}
const remaining = backup.remaining;
const copied = totalPages - remaining;
const progress = (copied / totalPages) * 100;
// Report progress
if (progressCallback) {
progressCallback(progress, copied, totalPages);
}
if (backup.completed) {
backup.finish((err) => {
if (err) {
return completeCallback(err);
}
completeCallback(null, 'Backup completed successfully');
});
} else if (backup.failed) {
backup.finish();
completeCallback(new Error('Backup failed'));
} else {
// Continue with next batch
setTimeout(stepWithProgress, 50); // Small delay
}
});
}
stepWithProgress();
});
}
// Usage
createProgressiveBackup(
db,
'progressive-backup.db',
(progress, copied, total) => {
console.log(`Backup progress: ${progress.toFixed(1)}% (${copied}/${total} pages)`);
},
(err, message) => {
if (err) {
console.error('Progressive backup failed:', err.message);
} else {
console.log(message);
}
db.close();
}
);function restoreFromBackup(targetDb, backupFile, callback) {
// Note: filenameIsDest = false means we're reading FROM the backupFile
const restore = targetDb.backup(backupFile, 'main', 'main', false, (err) => {
if (err) {
return callback(err);
}
console.log('Starting restore operation');
// Restore all pages at once
restore.step(-1, (err) => {
if (err) {
restore.finish();
return callback(err);
}
if (restore.completed) {
restore.finish((err) => {
if (err) {
return callback(err);
}
callback(null, 'Restore completed successfully');
});
} else {
restore.finish();
callback(new Error('Restore did not complete'));
}
});
});
}
// Usage
const targetDb = new sqlite3.Database(':memory:'); // Or any target database
restoreFromBackup(targetDb, 'my-backup.db', (err, message) => {
if (err) {
console.error('Restore failed:', err.message);
} else {
console.log(message);
// Verify restore by querying the restored database
targetDb.get("SELECT COUNT(*) as count FROM sqlite_master WHERE type='table'", (err, row) => {
if (err) {
console.error('Verification failed:', err.message);
} else {
console.log(`Restored database contains ${row.count} tables`);
}
targetDb.close();
});
}
});function createConcurrentBackup(sourceDb, backupFile, callback) {
const backup = sourceDb.backup(backupFile);
// Configure for busy databases
backup.retryErrors = [sqlite3.BUSY, sqlite3.LOCKED];
function performStep() {
// Copy small chunks to avoid blocking
backup.step(10, (err) => {
if (err) {
// Check if this is a retryable error
if (backup.retryErrors.includes(err.errno)) {
console.log('Database busy, retrying...');
setTimeout(performStep, 100); // Retry after delay
return;
}
backup.finish();
return callback(err);
}
if (backup.completed) {
backup.finish((err) => {
callback(err, 'Concurrent backup completed');
});
} else if (backup.failed) {
backup.finish();
callback(new Error('Concurrent backup failed'));
} else {
// Continue with next step, allowing other operations
setImmediate(performStep);
}
});
}
performStep();
}Install with Tessl CLI
npx tessl i tessl/npm-sqlite3