
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
prisma-do-backup
Advanced tools
Automated database backup system for Prisma projects with DigitalOcean Spaces storage
📦 Automated database backup system for Prisma projects with DigitalOcean Spaces storage.
npm install prisma-do-backup
# .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
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`);
# Create backup
npx prisma-do-backup backup
# List backups
npx prisma-do-backup list
# Download latest
npx prisma-do-backup download latest
# Cleanup (dry run)
npx prisma-do-backup cleanup --dry-run
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):
{
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
}
backup(options?)Create a new backup.
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.
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.
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.
await backup.deleteBackup('backup_production_2025-01-01.json');
cleanup(options?)Clean up old backups based on retention policy.
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.
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.
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.
const preview = await backup.previewRestore('latest');
getAvailableModels()Get list of available Prisma models.
const models = backup.getAvailableModels();
// ['User', 'Order', 'Product', ...]
prisma-do-backup <command> [options]
Commands:
backup Create a new backup
restore <file> Restore from backup (⚠️ use --no-dry-run to execute)
list List all available backups
download <file> Download a backup (use 'latest' for most recent)
delete <file> 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=<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> Path to .env file (default: .env.local or .env)
--prisma=<path> Path to Prisma client
--debug Show debug information
# Create backup
prisma-do-backup backup
# Backup specific tables
prisma-do-backup backup --tables=User,Order,Product
# List all backups
prisma-do-backup list
# Download latest backup
prisma-do-backup download latest
# Download specific backup
prisma-do-backup download backup_production_2025-01-20.json
# Delete a backup
prisma-do-backup delete backup_production_2025-01-01.json
# Cleanup (preview)
prisma-do-backup cleanup --dry-run
# Cleanup (execute)
prisma-do-backup cleanup
# Test connection
prisma-do-backup test
# Use custom .env file
prisma-do-backup backup --env=.env.production
// 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 }
);
}
}
// vercel.json
{
"crons": [
{
"path": "/api/cron/backup",
"schedule": "0 4 * * *"
}
]
}
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);
});
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`);
});
📖 Guida Completa: Per istruzioni dettagliate passo-passo, consulta docs/DIGITALOCEAN_SETUP_GUIDE.md
Crea un Account DigitalOcean:
Crea uno Space:
fra1 per Frankfurt, Europa)myapp-backups)Genera API Keys:
Configura le Variabili d'Ambiente:
# .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
| 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 |
Per un backup tipico (< 1GB): meno di $0.10/mese
The restore function includes multiple safety measures:
# Preview restore (dry-run - safe, no changes made)
npx prisma-do-backup restore latest
# Execute restore (⚠️ modifies database!)
npx prisma-do-backup restore latest --no-dry-run
# Restore specific tables only
npx prisma-do-backup restore latest --no-dry-run --tables=User,Order
# Restore with insert mode (skip existing records)
npx prisma-do-backup restore latest --no-dry-run --mode=insert
# Restore with deleteAndCreate mode (⚠️ deletes all existing data first!)
npx prisma-do-backup restore latest --no-dry-run --mode=deleteAndCreate
# Skip safety backup (dangerous!)
npx prisma-do-backup restore latest --no-dry-run --no-safety-backup
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}`);
| 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) |
# 1. Create backup from old database
DATABASE_URL=old_db_url npx prisma-do-backup backup
# 2. Preview restore to new database
DATABASE_URL=new_db_url npx prisma-do-backup restore latest
# 3. Execute restore
DATABASE_URL=new_db_url npx prisma-do-backup restore latest --no-dry-run
# 4. 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
MIT
Contributions are welcome! Please open an issue or PR.
FAQs
Automated database backup system for Prisma projects with DigitalOcean Spaces storage
The npm package prisma-do-backup receives a total of 18 weekly downloads. As such, prisma-do-backup popularity was classified as not popular.
We found that prisma-do-backup demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.