Intro
Backing up and restoring a REST API is not a single command but a set of operational practices that move you from observed problem to verified result. A backup is only useful if you can restore it, and a restore is only trustworthy if you can prove it worked. This article provides a practical, step-by-step guide for developers, DevOps consultants, and technical startup teams who need to implement reliable backup and restore procedures for a REST API built with Node.js, Express, and MongoDB. We will use concrete examples, commands, expected outputs, failure signals, and recovery decisions to make the process operational.
The primary goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify every result, and document recovery paths. We will cover five core practices:
- Version and Environment Inventory: Know exactly what you are running before you touch anything.
- Safe Configuration Path: Make changes in a controlled, reversible way.
- Verification and Diagnostics: Confirm that backups and restores actually work.
- Failure Modes and Recovery: Plan for the inevitable and know how to roll back.
- Operations Checklist: Turn these practices into a repeatable procedure.
Throughout, we will use a fictional e-commerce API called acme-api to illustrate real-world scenarios.
Version and Environment Inventory
Before any backup or restore operation, you must know the exact state of your environment. This includes the API application code, its dependencies, the runtime, the database, and any ancillary services. A version mismatch can turn a routine restore into a multi-hour outage. Inventory captures current state and timestamps, protects credentials, and defines the smallest justified change.
Identifying Components and Versions
For our acme-api, the components are:
- Application code: The Express.js server code, versioned in Git.
- Node.js runtime: The version installed on the production server.
- Package dependencies: The
node_moduleslockfile. - Database: MongoDB, including its data and schema.
- Environment configuration: Secret keys, database URLs, and other settings.
Use read-only commands to capture the current state without altering anything. For example, on the production server, run:
node --version
# Expected output: v18.16.0
npm --version
# Expected output: 9.5.1
git rev-parse HEAD
# Expected output: 3f2b9a4c1e... (commit hash)
mongod --version | head -1
# Expected output: db version v6.0.5
For the application itself, verify the deployed package versions:
npm list --depth=0
# Expected output shows: [email protected], [email protected], etc.
Document these versions in a text file or a configuration management database. Include the date and time of the inventory. For example:
2025-03-01 10:23:45 UTC, acme-api v1.4.2, Node v18.16.0, MongoDB v6.0.5, commit 3f2b9a4c1e, express 4.18.2, mongoose 7.1.0
Prerequisites and Blast Radius
Define prerequisites for any backup or restore operation. For our API, a full backup requires:
- Read access to the application files (usually via SSH or deployment tool).
- Read access to the MongoDB instance (a user with
backuprole). - Sufficient disk space for the backup.
- Ability to run commands without causing downtime (or a maintenance window if downtime is required).
Blast radius is the set of resources that a change could affect. For a database backup, the blast radius is typically just the database server, but if you stop the API to take a consistent backup, the blast radius includes API availability. Always strive to minimize blast radius by using non-disruptive backup methods, such as MongoDB's mongodump with --oplog for point-in-time backups without stopping the server.
Capturing Configuration State
Environment variables and configuration files are critical. Use a secure method to export them, but never output secrets in plain text in logs or articles. For example, to capture the environment variables without revealing values:
env | cut -d= -f1 | sort > env_keys.txt
# Expected output: a file listing variable names only, e.g., DATABASE_URL, JWT_SECRET, etc.
For MongoDB, capture user roles and permissions:
mongosh admin --eval 'db.getUsers()'
# Expected output: array of user documents (sensitive; handle carefully)
Store these inventories in a secure location, such as a password manager or encrypted backup system.
Verification Step
After inventory, verify you can access all components with read-only commands. For example, check that the API is responding:
curl -s http://localhost:3000/health
# Expected output: {"status":"ok","version":"1.4.2"}
Check MongoDB connectivity:
mongosh --eval 'db.runCommand({ ping: 1 })'
# Expected output: { ok: 1 }
If any verification fails, stop and resolve before proceeding. Recovery path: if you cannot access a component, check credentials, network, and service status, then re-run verification.
Safe Configuration Path
Making configuration changes safely means you can undo them if something goes wrong. This section covers how to apply changes to the API or its environment with minimal risk.
Version Control and Configuration Files
All configuration changes should be in version control. For acme-api, we store configuration in a config directory with files per environment, e.g., config/production.json. When changing a setting, follow these steps:
- Branch: Create a Git branch for the change.
git checkout -b update-db-timeout
- Edit: Modify the file with the new value.
{
"database": {
"url": "mongodb://db.internal:27017/acme",
"options": {
"serverSelectionTimeoutMS": 5000
}
}
}
- Test: Run the test suite locally or in a staging environment.
npm test
# Expected output: all tests pass
- Deploy: Merge and deploy using your CI/CD pipeline.
- Verify: After deployment, run the health check and log checks to confirm the new setting is in effect.
Environment Variables
For secrets and deployment-specific values, use environment variables. To rotate a database password safely:
- Generate a new password using a secure method:
openssl rand -base64 24
# Output: a random string
- Update the secret in your secret manager (e.g., AWS Secrets Manager, HashiCorp Vault) and in MongoDB.
- Change the environment variable on the API server, for example, by updating the systemd unit file or container configuration.
- Restart the API process:
sudo systemctl restart acme-api
- Verify the API is running and can connect to the database:
curl -s http://localhost:3000/health
# Expected output: {"status":"ok"}
- If verification fails, roll back to the previous environment variable value and restart again.
Database Schema Changes
Changing the database schema during an upgrade can be risky. Use a migration tool like migrate-mongo or mongo-migrate. For example, to add an index:
- Create a migration script:
// migrations/20250301120000-add-order-index.js
module.exports = {
async up(db) {
await db.collection('orders').createIndex({ customerId: 1, createdAt: -1 });
},
async down(db) {
await db.collection('orders').dropIndex('customerId_1_createdAt_-1');
}
};
- Run the migration:
migrate-mongo up
# Expected output: Applied 1 migration
- Verify the index exists:
mongosh acme --eval 'db.orders.getIndexes()'
# Check for the new index
- If needed, roll back with
migrate-mongo down.
Safe Configuration Checklist
| Item | Action | Verification |
|---|---|---|
| Config change | Create branch, edit file, test | Tests pass in staging |
| Secret rotation | Update secret store and env var, restart | API health check OK |
| Schema migration | Run migration up | Index exists, queries work |
| Dependency update | Update package.json, run tests, deploy | All tests pass, no regression |
Verification and Diagnostics
Verification is not just checking that a command succeeded; it is confirming that the backup or restore meets your recovery objectives. This section details how to test backups and restores without risking production data.
Backup Verification
After taking a backup, you must verify its integrity and recoverability. For MongoDB, a backup taken with mongodump can be checked by listing files and optionally restoring to a temporary instance.
- Create a backup:
mongodump --uri="mongodb://backupUser:[email protected]:27017/acme" --archive=/backups/acme-20250301-1000.archive
# Expected output: done
- Verify the archive exists and is non-empty:
ls -lh /backups/acme-20250301-1000.archive
# Expected output: -rw-r--r-- 1 backup backup 2.3G Mar 1 10:00 acme-20250301-1000.archive
- To test restore without affecting production, spin up a temporary MongoDB instance in a container or separate server, restore the archive, and run queries to ensure data integrity.
# Start temporary MongoDB (using Docker)
docker run --name temp-mongo -d -p 27018:27017 mongo:6.0.5
# Restore the archive
mongorestore --uri="mongodb://localhost:27018" --archive=/backups/acme-20250301-1000.archive
# Expected output: finished restoring acme.orders (1000 documents)
# Query to verify
mongosh --port 27018 --eval 'db.orders.countDocuments()'
# Expected output: 1000
# Clean up
docker stop temp-mongo && docker rm temp-mongo
This process ensures your backup is not corrupt and can be restored.
API Restore Verification
If you need to restore the API itself (code and configuration), follow a similar validation:
- Restore application files from Git or backup archive to a staging directory.
- Install dependencies:
npm ci
# Expected output: added 123 packages
- Run the application with the restored configuration in a staging environment.
- Run integration tests against the staging API, including critical endpoints.
- Compare response outputs with expected schemas.
Point-in-Time Recovery Testing
For disaster recovery, you may need to restore to a specific point in time. MongoDB supports point-in-time recovery using oplog. Test this by:
- Enable oplog backups in your backup strategy (
mongodump --oplog). - In a test environment, restore the full backup and then apply oplog entries up to a specific timestamp using
mongorestore --oplogReplay --oplogLimit. - Verify that the data matches the state at that timestamp by checking a known record or count.
Diagnostic Commands and Expected Outputs
When verification fails, you need to diagnose. Common commands include:
- Check API logs:
tail -f /var/log/acme-api.log - Check process status:
systemctl status acme-api - Check database connectivity:
mongosh --eval 'db.runCommand({ ping: 1 })' - Check disk space:
df -h
If the API cannot connect to the database, logs may show MongooseServerSelectionError: connect ECONNREFUSED. Check whether MongoDB is running (systemctl status mongod), and if not, start it.
Failure Modes and Recovery
Anticipating failure modes is essential. This section outlines common failures and recovery steps.
Backup Failure
A backup may fail due to insufficient disk space, permission errors, or network interruption.
- Symptom:
mongodumpexits with errorE: not enough free space. - Recovery: Free up space or specify a different backup destination with more space. Then rerun.
- Prevention: Monitor disk usage and set alerts.
Restore Failure
Restore can fail if the backup is corrupt, version mismatch, or not enough memory.
- Symptom:
mongorestoreerrorFailed: restore error: error applying oplog. - Recovery: Verify backup integrity with checksums if available. Try restoring without oplog. If version mismatch, ensure the target MongoDB version is compatible.
- Fallback: If restore fails completely, use a previous known-good backup.
API Downtime During Recovery
If the API is down, restore may require starting from scratch.
- Symptom: API returns 503 Service Unavailable.
- Recovery: Deploy the last known good version from Git, restore the database, and then perform a health check.
- Rollback Plan: Keep the previous release artifacts ready for quick rollback.
Data Corruption
Data corruption can occur due to disk errors or bugs.
- Symptom: Queries return unexpected results or errors like
Unrecognized pipeline stage name: '$sort'. - Recovery: Restore from the most recent verified backup, then apply incremental backups or oplog to minimize data loss.
- Prevention: Enable MongoDB replication for redundancy and take frequent backups.
Running a Fire Drill
Regularly test your backup and restore process. Schedule a quarterly fire drill where you simulate a total failure and recover in a staging environment. Document the time taken and any issues.
Operations Checklist
Use this checklist before and after any backup or restore operation to ensure consistency and safety.
Pre-Operation Checklist
- [ ] Confirm environment inventory is up to date (versions, configurations).
- [ ] Verify there is sufficient disk space for the backup.
- [ ] Ensure you have the necessary credentials (stored securely, not in plain text).
- [ ] Notify stakeholders if any downtime is expected.
- [ ] Perform a read-only health check on the API and database.
Backup Checklist
- [ ] Run backup command with correct parameters.
- [ ] Record the backup file location, size, and timestamp.
- [ ] Verify backup file integrity (e.g.,
mongodumpsuccess output). - [ ] Copy backup to a separate location (off-site or cloud storage).
- [ ] Log the backup in an operations journal.
Restore Checklist
- [ ] Identify the backup to restore from and the reason.
- [ ] If possible, restore to a staging environment first.
- [ ] Perform restore using appropriate commands.
- [ ] Verify data consistency (documents count, sample queries).
- [ ] Run API integration tests against the restored database.
- [ ] Switch traffic to the restored API if applicable.
- [ ] Monitor for errors after restore.
Post-Operation Review
After any operation, review:
- What worked well?
- What could be improved?
- Were there any unexpected issues?
- Document lessons learned and update runbooks.
Conclusion
REST API backup and restore is only effective when every step is version-scoped, observable, and reversible where possible. Copying commands without understanding prerequisites and expected outputs is not an operations procedure; it is a gamble. By following the practices outlined in this article—from meticulous environment inventory to thorough verification and recovery testing—you can build a robust backup and restore system that minimizes downtime and data loss.
As a next step, choose one low-risk verification from this article, such as running a read-only health check on your API or verifying a recent database backup. Record the current state, run the documented command, compare the result with the expected signal, and review dependencies like Express, Node.js, and MongoDB. Over time, expand your checks to cover full restore drills. A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision.