Skip to content

Database Backups

backupx provides comprehensive database backup support for SQLite, PostgreSQL, and MySQL databases, with advanced features for filtering, compression, and connection management.

Supported Databases

SQLite

SQLite support is built directly into Bun, providing fast and reliable backups:

ts
const sqliteConfig = {
  type: BackupType.SQLITE,
  name: 'app-database',
  path: './database.sqlite',
  compress: true,
}

Features:

  • ✅ Direct file-based backup
  • ✅ No additional dependencies
  • ✅ Complete schema and data export
  • ✅ Supports WAL mode databases
  • ✅ Atomic backup operations

PostgreSQL

Full PostgreSQL support with flexible connection options:

ts
// Connection string
const postgresConfig1 = {
  type: BackupType.POSTGRESQL,
  name: 'main-db',
  connection: 'postgres://user:pass@localhost:5432/myapp',
  includeSchema: true,
  includeData: true,
}

// Connection object
const postgresConfig2 = {
  type: BackupType.POSTGRESQL,
  name: 'analytics',
  connection: {
    hostname: 'pg.example.com',
    port: 5432,
    database: 'analytics',
    username: 'backup_user',
    password: process.env.PG_PASSWORD,
    ssl: true,
  },
}

Features:

  • ✅ Connection strings and objects
  • ✅ SSL/TLS support
  • ✅ Schema-only or data-only backups
  • ✅ Table filtering (include/exclude)
  • ✅ Custom port and authentication

MySQL

Complete MySQL and MariaDB compatibility:

ts
const mysqlConfig = {
  type: BackupType.MYSQL,
  name: 'legacy-app',
  connection: {
    hostname: 'mysql.example.com',
    port: 3306,
    database: 'legacy_app',
    username: 'backup_user',
    password: process.env.MYSQL_PASSWORD,
    ssl: false,
  },
  includeSchema: true,
  includeData: true,
}

Features:

  • ✅ MySQL 5.7+ and MariaDB support
  • ✅ SSL connection support
  • ✅ Custom authentication methods
  • ✅ Table filtering capabilities
  • ✅ Charset and collation preservation

Advanced Configuration

Table Filtering

Control which tables are included in your backups:

ts
const filterConfig = {
  type: BackupType.POSTGRESQL,
  name: 'user-data',
  connection: process.env.DATABASE_URL,

  // Include only specific tables
  tables: ['users', 'profiles', 'settings'],

  // Or exclude specific tables
  excludeTables: ['sessions', 'logs', 'cache', 'password_resets'],

  includeSchema: true,
  includeData: true,
}

Schema vs Data

Choose what to include in your backups:

ts
// Schema only (structure, no data)
const schemaOnlyConfig = {
  type: BackupType.MYSQL,
  name: 'schema-backup',
  connection: connectionConfig,
  includeSchema: true,
  includeData: false, // No data
}

// Data only (no schema)
const dataOnlyConfig = {
  type: BackupType.MYSQL,
  name: 'data-backup',
  connection: connectionConfig,
  includeSchema: false, // No schema
  includeData: true,
}

// Complete backup (default)
const fullBackupConfig = {
  type: BackupType.MYSQL,
  name: 'full-backup',
  connection: connectionConfig,
  includeSchema: true,
  includeData: true,
}

Connection Security

Secure your database connections:

ts
// PostgreSQL with SSL
const securePostgresConfig = {
  type: BackupType.POSTGRESQL,
  name: 'secure-db',
  connection: {
    hostname: 'secure-pg.example.com',
    port: 5432,
    database: 'production',
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    ssl: true, // Enable SSL
  },
}

// MySQL with SSL
const secureMySQLConfig = {
  type: BackupType.MYSQL,
  name: 'secure-mysql',
  connection: {
    hostname: 'secure-mysql.example.com',
    port: 3306,
    database: 'app',
    username: process.env.MYSQL_USER,
    password: process.env.MYSQL_PASSWORD,
    ssl: true,
  },
}

Custom Output Naming

Control backup file naming:

ts
const customNameConfig = {
  type: BackupType.SQLITE,
  name: 'app-db',
  path: './app.sqlite',
  filename: 'daily-backup', // Custom base name
  // Results in: daily-backup_2023-12-01T10-30-00.sql
}

Production Examples

Multi-Database Setup

Backup multiple databases with different configurations:

ts
const config: BackupConfig = {
  verbose: true,
  outputPath: './production-backups',

  databases: [
    // Main application database
    {
      type: BackupType.POSTGRESQL,
      name: 'app-main',
      connection: process.env.PRIMARY_DATABASE_URL!,
      includeSchema: true,
      includeData: true,
      excludeTables: [
        'sessions', // Session data
        'password_resets', // Temporary tokens
        'failed_jobs', // Job queue data
        'cache', // Application cache
      ],
      compress: true,
    },

    // Analytics database (data only)
    {
      type: BackupType.POSTGRESQL,
      name: 'analytics-data',
      connection: process.env.ANALYTICS_DATABASE_URL!,
      includeSchema: false, // Schema rarely changes
      includeData: true,
      tables: ['events', 'user_analytics', 'page_views'],
      compress: true,
    },

    // Configuration database (schema + data)
    {
      type: BackupType.MYSQL,
      name: 'config-db',
      connection: {
        hostname: process.env.CONFIG_DB_HOST!,
        database: 'app_config',
        username: process.env.CONFIG_DB_USER!,
        password: process.env.CONFIG_DB_PASSWORD!,
        ssl: true,
      },
      includeSchema: true,
      includeData: true,
      compress: false, // Small database, compression not needed
    },

    // Local SQLite cache
    {
      type: BackupType.SQLITE,
      name: 'local-cache',
      path: './storage/cache.sqlite',
      compress: true,
    },
  ],

  files: [], // No file backups in this config

  retention: {
    count: 14, // Keep 2 weeks
    maxAge: 90, // Delete after 90 days
  }
}

Development Environment

Simplified setup for development:

ts
const devConfig: BackupConfig = {
  verbose: true,
  outputPath: './dev-backups',

  databases: [
    {
      type: BackupType.SQLITE,
      name: 'dev-db',
      path: './dev.sqlite',
      compress: false, // Faster for frequent backups
    },

    {
      type: BackupType.POSTGRESQL,
      name: 'test-db',
      connection: 'postgres://localhost:5432/test_db',
      includeSchema: true,
      includeData: true,
      // No table filtering for dev environment
    },
  ],

  retention: {
    count: 5, // Keep only recent backups
  }
}

Error Handling

Database backups can fail for various reasons. backupx provides detailed error information:

ts
const manager = new BackupManager(config)
const summary = await manager.createBackup()

// Check for database backup failures
const dbFailures = summary.databaseBackups.filter(r => !r.success)

for (const failure of dbFailures) {
  console.error(`Database backup failed: ${failure.name}`)
  console.error(`Error: ${failure.error}`)

  // Common error types:
  if (failure.error?.includes('connection')) {
    console.log('💡 Check database connection settings')
  }
  else if (failure.error?.includes('permission')) {
    console.log('💡 Check database user permissions')
  }
  else if (failure.error?.includes('not found')) {
    console.log('💡 Verify database exists and path is correct')
  }
}

Common Issues

Connection Failures:

ts
// ❌ Bad: Hardcoded localhost
const badConfig = {
  connection: 'postgres://user:pass@localhost:5432/db'
}

// ✅ Good: Environment-based
const goodConfig = {
  connection: process.env.DATABASE_URL || 'postgres://localhost:5432/dev_db'
}

Permission Issues:

sql
-- Grant backup permissions for PostgreSQL
GRANT SELECT ON ALL TABLES IN SCHEMA public TO backup_user;
GRANT USAGE ON SCHEMA public TO backup_user;

-- Grant backup permissions for MySQL
GRANT SELECT ON database_name.* TO 'backup_user'@'%';

SSL Certificate Problems:

ts
// For development with self-signed certificates
const devConnectionConfig = {
  connection: {
    hostname: 'localhost',
    database: 'myapp',
    username: 'user',
    password: 'pass',
    ssl: false, // Disable SSL for development
  }
}

Performance Optimization

Large Database Backups

For large databases, consider these optimizations:

ts
const largeDbConfig = {
  type: BackupType.POSTGRESQL,
  name: 'large-db',
  connection: connectionConfig,

  // Exclude large tables that change frequently
  excludeTables: [
    'logs',
    'audit_trail',
    'temporary_data',
    'search_index',
  ],

  // Use compression for large datasets
  compress: true,

  // Backup schema separately for faster restores
  includeSchema: true,
  includeData: true,
}

// Separate data-only backup for frequently changing data
const dataOnlyLargeConfig = {
  type: BackupType.POSTGRESQL,
  name: 'large-db-data-only',
  connection: connectionConfig,
  tables: ['users', 'orders', 'products'], // Only essential tables
  includeSchema: false,
  includeData: true,
  compress: true,
}

Parallel Backups

Run multiple database backups concurrently:

ts
import { backupMySQL, backupPostgreSQL, backupSQLite } from 'backupx'

// Manual parallel execution
const backupPromises = [
  backupSQLite(sqliteConfig, outputPath),
  backupPostgreSQL(pgConfig, outputPath),
  backupMySQL(mysqlConfig, outputPath),
]

const results = await Promise.allSettled(backupPromises)

Backup Verification

Verify your database backups are valid:

ts
// After backup completion
const summary = await manager.createBackup()

for (const result of summary.databaseBackups) {
  if (result.success) {
    console.log(`✅ ${result.name}: ${formatBytes(result.size)}`)

    // Verify backup file exists and has content
    const backupPath = join(outputPath, result.filename)
    const stats = await stat(backupPath)

    if (stats.size === 0) {
      console.warn(`⚠️  Backup file is empty: ${result.filename}`)
    }
  }
}

function formatBytes(bytes: number): string {
  const sizes = ['Bytes', 'KB', 'MB', 'GB']
  if (bytes === 0)
    return '0 Bytes'
  const i = Math.floor(Math.log(bytes) / Math.log(1024))
  return `${Math.round(bytes / 1024 ** i * 100) / 100} ${sizes[i]}`
}

Next Steps

Released under the MIT License.