Automated database backup system for Prisma projects with DigitalOcean Spaces storage
npm install prisma-do-backup๐ฆ Automated database backup system for Prisma projects with DigitalOcean Spaces storage.
- ๐ Easy to use - Simple API and CLI
- ๐ Auto-detect tables - Automatically discovers Prisma models
- โ๏ธ DigitalOcean Spaces - S3-compatible cloud storage
- ๐งน Smart cleanup - Intelligent retention policy
- ๐ Progress tracking - Callbacks for monitoring
- ๐ Secure - Private storage with proper ACL
``bash`
npm install prisma-do-backup
`bash`.env
DO_SPACES_ENDPOINT=https://fra1.digitaloceanspaces.com
DO_SPACES_REGION=fra1
DO_SPACES_KEY=your_access_key
DO_SPACES_SECRET=your_secret_key
DO_SPACES_BUCKET=my-backups
`javascript
import { PrismaDoBackup } from 'prisma-do-backup';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const backup = new PrismaDoBackup(prisma);
// Create backup
const result = await backup.backup();
console.log(Backup created: ${result.filename});
// List backups
const list = await backup.listBackups();
console.log(Total backups: ${list.totalCount});
// Download latest backup
const data = await backup.downloadBackup('latest');
console.log(Downloaded: ${data.filename});
// Cleanup old backups
const cleanup = await backup.cleanup({ dryRun: true });
console.log(Would delete: ${cleanup.toDelete.length} backups);`
`bashCreate backup
npx prisma-do-backup backup
API Reference
$3
`javascript
const backup = new PrismaDoBackup(prisma, options);
`Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
|
endpoint | string | DO_SPACES_ENDPOINT | DigitalOcean Spaces endpoint |
| region | string | fra1 | DigitalOcean region |
| accessKeyId | string | DO_SPACES_KEY | Access key |
| secretAccessKey | string | DO_SPACES_SECRET | Secret key |
| bucket | string | database-backups | Bucket name |
| prefix | string | backups/ | Key prefix for backups |
| environment | string | NODE_ENV | Environment name |
| tables | string[] | auto-detect | Specific tables to backup |
| retention | object | see below | Retention policy |Retention Policy (default):
`javascript
{
keepAllDays: 30, // 0-30 days: keep all
keepWeeklyDays: 90, // 30-90 days: keep 1/week
keepMonthlyDays: 365, // 90-365 days: keep 1/month
keepQuarterlyDays: 730, // 365-730 days: keep 1/quarter
deleteAfterDays: 730, // Beyond: delete
}
`$3
####
backup(options?)Create a new backup.
`javascript
const result = await backup.backup({
tables: ['User', 'Order'], // Optional: specific tables
onProgress: ({ step, message }) => console.log(message),
});// Result:
{
success: true,
timestamp: '2025-01-20T12:00:00.000Z',
environment: 'production',
filename: 'backup_production_2025-01-20T12-00-00.json',
uploadSize: 1234567,
uploadUrl: 'https://...',
duration: 5432,
summary: {
totalTables: 25,
totalRecords: 10000,
exportedTables: [...],
errors: []
}
}
`####
listBackups()List all available backups.
`javascript
const list = await backup.listBackups();// Result:
{
backups: [
{
filename: 'backup_production_2025-01-20.json',
key: 'backups/backup_production_2025-01-20.json',
size: 1234567,
sizeMB: '1.18',
lastModified: '2025-01-20T12:00:00.000Z'
},
// ...
],
totalCount: 30,
totalSize: 37037010,
totalSizeMB: '35.32',
latest: { ... },
oldest: { ... }
}
`####
downloadBackup(filename)Download a backup. Use
'latest' for the most recent.`javascript
const data = await backup.downloadBackup('latest');// Result:
{
filename: 'backup_production_2025-01-20.json',
size: 1234567,
sizeMB: '1.18',
data: {
timestamp: '...',
environment: 'production',
tables: { User: [...], Order: [...] },
summary: { ... }
}
}
`####
deleteBackup(filename)Delete a specific backup.
`javascript
await backup.deleteBackup('backup_production_2025-01-01.json');
`####
cleanup(options?)Clean up old backups based on retention policy.
`javascript
const result = await backup.cleanup({
dryRun: true, // Simulate without deleting
});// Result:
{
success: true,
toKeep: ['backup_1.json', 'backup_2.json'],
toDelete: ['old_backup_1.json', 'old_backup_2.json'],
deleted: [], // Empty if dryRun
freedSpace: 5000000,
freedSpaceMB: '4.77',
dryRun: true,
retention: { ... }
}
`####
testConnection()Test connection to DigitalOcean Spaces.
`javascript
const result = await backup.testConnection();// Result:
{
success: true,
message: 'Connection successful',
bucket: 'my-backups',
endpoint: 'https://fra1.digitaloceanspaces.com',
region: 'fra1'
}
`####
restore(source, options?)Restore database from a backup. Dry-run by default for safety.
`javascript
const result = await backup.restore('latest', {
dryRun: true, // Preview only (default: true)
mode: 'upsert', // 'upsert', 'insert', 'deleteAndCreate'
tables: ['User', 'Order'], // Specific tables (optional)
createSafetyBackup: true, // Backup before restore (default: true)
continueOnError: false, // Stop on first error (default)
onProgress: ({ message }) => console.log(message),
});// Result:
{
success: true,
dryRun: false,
mode: 'upsert',
source: 'backup_production_2025-01-20.json',
safetyBackup: 'backup_production_2025-01-20T12-05-00.json',
tables: { total: 5, restored: 5, skipped: 0, failed: 0 },
records: { total: 1000, processed: 1000, created: 500, updated: 500, skipped: 0, failed: 0 },
details: [...],
errors: [],
duration: 5432
}
`####
previewRestore(source, options?)Alias for
restore() with dryRun: true. Always safe.`javascript
const preview = await backup.previewRestore('latest');
`####
getAvailableModels()Get list of available Prisma models.
`javascript
const models = backup.getAvailableModels();
// ['User', 'Order', 'Product', ...]
`CLI Reference
`bash
prisma-do-backup [options]Commands:
backup Create a new backup
restore Restore from backup (โ ๏ธ use --no-dry-run to execute)
list List all available backups
download Download a backup (use 'latest' for most recent)
delete Delete a specific backup
cleanup Clean up old backups based on retention policy
test Test connection to DigitalOcean Spaces
Options:
--dry-run Preview changes without modifying (default for restore)
--no-dry-run Execute restore (modifies database!)
--tables=t1,t2 Specific tables to backup/restore
--mode= Restore mode: upsert (default), insert, deleteAndCreate
--no-safety-backup Skip safety backup before restore
--continue-on-error Continue if some records fail
--env= Path to .env file (default: .env.local or .env)
--prisma= Path to Prisma client
--debug Show debug information
`$3
`bash
Create backup
prisma-do-backup backupBackup specific tables
prisma-do-backup backup --tables=User,Order,ProductList all backups
prisma-do-backup listDownload latest backup
prisma-do-backup download latestDownload specific backup
prisma-do-backup download backup_production_2025-01-20.jsonDelete a backup
prisma-do-backup delete backup_production_2025-01-01.jsonCleanup (preview)
prisma-do-backup cleanup --dry-runCleanup (execute)
prisma-do-backup cleanupTest connection
prisma-do-backup testUse custom .env file
prisma-do-backup backup --env=.env.production
`Integration Examples
$3
`javascript
// app/api/cron/backup/route.js
import { NextResponse } from 'next/server';
import { PrismaDoBackup } from 'prisma-do-backup';
import prisma from '@/lib/prisma';export async function GET(request) {
// Verify cron secret in production
if (process.env.NODE_ENV === 'production') {
const authHeader = request.headers.get('authorization');
if (authHeader !==
Bearer ${process.env.CRON_SECRET}) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
} try {
const backup = new PrismaDoBackup(prisma);
const result = await backup.backup();
return NextResponse.json(result);
} catch (error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
);
}
}
`$3
`json
// vercel.json
{
"crons": [
{
"path": "/api/cron/backup",
"schedule": "0 4 *"
}
]
}
`$3
`javascript
import express from 'express';
import { PrismaDoBackup } from 'prisma-do-backup';
import { PrismaClient } from '@prisma/client';const app = express();
const prisma = new PrismaClient();
app.post('/admin/backup', async (req, res) => {
const backup = new PrismaDoBackup(prisma);
const result = await backup.backup();
res.json(result);
});
app.get('/admin/backups', async (req, res) => {
const backup = new PrismaDoBackup(prisma);
const list = await backup.listBackups();
res.json(list);
});
`$3
`javascript
import cron from 'node-cron';
import { PrismaDoBackup } from 'prisma-do-backup';
import { PrismaClient } from '@prisma/client';const prisma = new PrismaClient();
// Daily backup at 4 AM
cron.schedule('0 4 *', async () => {
console.log('Starting scheduled backup...');
const backup = new PrismaDoBackup(prisma);
const result = await backup.backup();
console.log(
Backup completed: ${result.filename});
});// Weekly cleanup on Sunday at 5 AM
cron.schedule('0 5 0', async () => {
console.log('Starting scheduled cleanup...');
const backup = new PrismaDoBackup(prisma);
const result = await backup.cleanup();
console.log(
Cleanup completed: ${result.deleted.length} files deleted);
});
`DigitalOcean Spaces Setup
> ๐ Guida Completa: Per istruzioni dettagliate passo-passo, consulta docs/DIGITALOCEAN_SETUP_GUIDE.md
$3
1. Crea un Account DigitalOcean:
- Vai su digitalocean.com e registrati
- Puoi ottenere $200 di crediti gratuiti per i primi 60 giorni
2. Crea uno Space:
- Dashboard โ Spaces Object Storage โ Create a Space
- Scegli la regione (es:
fra1 per Frankfurt, Europa)
- Scegli un nome unico (es: myapp-backups)
- Seleziona "Restrict File Listing" per sicurezza3. Genera API Keys:
- Dashboard โ API โ Spaces Keys โ Generate New Key
- IMPORTANTE: Copia subito Access Key e Secret Key (mostrate una sola volta!)
4. Configura le Variabili d'Ambiente:
`bash
.env
DO_SPACES_ENDPOINT=https://fra1.digitaloceanspaces.com
DO_SPACES_REGION=fra1
DO_SPACES_KEY=DO00XXXXXXXXXXXXXX
DO_SPACES_SECRET=la-tua-secret-key
DO_SPACES_BUCKET=myapp-backups
`$3
| Regione | Codice | Endpoint |
|---------|--------|----------|
| Frankfurt |
fra1 | https://fra1.digitaloceanspaces.com |
| Amsterdam | ams3 | https://ams3.digitaloceanspaces.com |
| New York | nyc3 | https://nyc3.digitaloceanspaces.com |
| San Francisco | sfo3 | https://sfo3.digitaloceanspaces.com |
| Singapore | sgp1 | https://sgp1.digitaloceanspaces.com |
| Sydney | syd1 | https://syd1.digitaloceanspaces.com |$3
- Storage: $0.02/GB al mese
- Transfer OUT: $0.01/GB (primi 1TB gratuiti)
- Transfer IN: Gratuito
Per un backup tipico (< 1GB): meno di $0.10/mese
Restore from Backup
$3
The restore function includes multiple safety measures:
1. Dry-run by default - Preview what will change before executing
2. Automatic safety backup - Creates a backup of current database before restore
3. Upsert mode - Updates existing records, creates new ones (no duplicates)
4. Transaction-safe - Errors are handled gracefully
5. 5-second warning - Time to abort before live restore
$3
`bash
Preview restore (dry-run - safe, no changes made)
npx prisma-do-backup restore latestExecute restore (โ ๏ธ modifies database!)
npx prisma-do-backup restore latest --no-dry-runRestore specific tables only
npx prisma-do-backup restore latest --no-dry-run --tables=User,OrderRestore with insert mode (skip existing records)
npx prisma-do-backup restore latest --no-dry-run --mode=insertRestore with deleteAndCreate mode (โ ๏ธ deletes all existing data first!)
npx prisma-do-backup restore latest --no-dry-run --mode=deleteAndCreateSkip safety backup (dangerous!)
npx prisma-do-backup restore latest --no-dry-run --no-safety-backup
`$3
`javascript
import { PrismaDoBackup } from 'prisma-do-backup';
import { PrismaClient } from '@prisma/client';const prisma = new PrismaClient();
const backup = new PrismaDoBackup(prisma);
// Preview restore (dry-run)
const preview = await backup.restore('latest');
console.log(
Would restore ${preview.records.total} records);// Execute restore
const result = await backup.restore('latest', {
dryRun: false, // Execute for real
mode: 'upsert', // 'upsert', 'insert', or 'deleteAndCreate'
tables: ['User', 'Order'], // Specific tables (optional)
createSafetyBackup: true, // Create backup first (default: true)
continueOnError: false, // Stop on first error (default)
onProgress: ({ message }) => console.log(message),
});
console.log(
Restored ${result.records.processed} records);
console.log(Safety backup: ${result.safetyBackup});
`$3
| Mode | Description | Use Case |
|------|-------------|----------|
|
upsert (default) | Update if exists, create if not | Safe migration, sync data |
| insert | Insert only, skip existing | Add missing records only |
| deleteAndCreate | Delete all, then insert | Full reset (โ ๏ธ dangerous) |$3
`bash
1. Create backup from old database
DATABASE_URL=old_db_url npx prisma-do-backup backup2. Preview restore to new database
DATABASE_URL=new_db_url npx prisma-do-backup restore latest3. Execute restore
DATABASE_URL=new_db_url npx prisma-do-backup restore latest --no-dry-run4. If something goes wrong, restore from safety backup
DATABASE_URL=new_db_url npx prisma-do-backup restore backup_safety_xxx.json --no-dry-run
``1. Never commit credentials - Use environment variables
2. Use private ACL - Backups are stored with private access
3. Rotate keys regularly - Change Spaces keys every 6 months
4. Limit permissions - Use keys with minimal required permissions
5. Encrypt sensitive data - Consider encrypting backup files
- README - Guida rapida e API reference
- DigitalOcean Setup Guide - Configurazione completa passo-passo
MIT
Contributions are welcome! Please open an issue or PR.