bubble-io-cli
v4.6.1
Published
An open-source CLI for managing, backing up, and interacting with Bubble.io applications via the Data API
Maintainers
Readme
🫧 bubble-io-cli
A powerful, open-source command-line interface for developers and entrepreneurs who build with Bubble.io.
✨ What is bubble-io-cli?
bubble-io-cli brings your Bubble.io application to the terminal. Instead of navigating the Bubble dashboard to export data or manage configurations, you can now automate these workflows directly from the command line — perfect for CI/CD pipelines, scheduled backups, developer tooling, and no-code + code hybrid workflows.
# Launch the Interactive Terminal UI (TUI) — No complex arguments needed!
bubble-io-cli
# Or run direct commands:
# Configure credentials
bubble-io-cli config --app my-cool-app --key YOUR_BUBBLE_API_KEY
# Back up any data type instantly
bubble-io-cli backup --type Product --env version-live
# Generate a GitHub Actions workflow for nightly automated backups
bubble-io-cli generate ci --provider github
# Export Bubble data directly into a SQLite database
bubble-io-cli export db --type User --target sqlite --db ./bubble.db
# Compare schema between environments
bubble-io-cli schema diff
# Generate database migrations declaratively (New in v4.4.0)
bubble-io-cli schema migrate:generate --name add_orders
# Use lifecycle hooks to auto-upload backups to S3 (New in v4.5.0)
# Place a plugin in ~/.bubble-cli/plugins/ — hooks fire automatically
# Start a local mock server for offline testing
bubble-io-cli mock --file ./backup-product.json --port 3333🚀 Quick Start
Prerequisites
- Node.js >= 18.0.0
- A Bubble.io account with API access enabled
- Your Bubble Private API Key (found in Settings → API → Private key)
Installation
# Install globally via npm
npm install -g bubble-io-cli
# Or use directly without installing
npx bubble-io-cli --help🎮 Interactive Mode (TUI)
Don't want to remember command-line arguments and flags? Run bubble-io-cli with no arguments to launch the beautiful, interactive Terminal User Interface (TUI):
bubble-io-cli┌ 🫧 Bubble.io CLI — Interactive Mode
│
◇ Active Environment
│ Profile: default
│ App: my-cool-app.bubbleapps.io
│ API Key: ********************1234
│
? What would you like to do?
│ ● 📐 Schema Operations (List, Diff, ERD, Migrations & Snapshots)
│ ○ 💾 Data Operations (Backup, Restore, Diff, Seed, REPL Query, DB Export)
│ ○ 🧬 Code & Type Generation (TypeScript interfaces, Templates, CI/CD)
│ ○ 🛠️ Developer Tools (PII Audit, Health check, Mock server, Workflows)
│ ○ ⚙️ Profiles & Credentials (Manage saved apps and API keys)
│ ○ ❌ Exit
└Key Highlights of Interactive Mode
- Zero-setup Discovery: Guided prompts for data types, environments (
version-testvsversion-live), output formats, and paths. - Smart Credential Management: Displays your active profile, subdomain, and masked API key on entry. If no profile exists, prompts to set one up on the spot.
- Continuous Workflow: Automatically asks if you'd like to perform another action after completing a task without re-running the command.
- Safe Exit & Cancellation: Press
Ctrl+CorEscat any prompt step to gracefully cancel without stack traces.
📖 Commands
config — Manage Credentials & Profiles
Store your Bubble app credentials securely in local OS config storage. Supports multiple named profiles so you can manage credentials for several Bubble apps simultaneously.
# Save credentials (default profile)
bubble-io-cli config --app my-cool-app --key YOUR_BUBBLE_API_KEY
# Save credentials under a named profile
bubble-io-cli config --app my-staging-app --key STAGING_KEY --profile staging
# List all stored profiles (shows active profile with ●)
bubble-io-cli config --list
# Switch the active profile
bubble-io-cli config --use staging
# View the current profile's configuration (key is masked)
bubble-io-cli config --show
# View a specific profile's configuration
bubble-io-cli config --show --profile staging
# Clear the current profile
bubble-io-cli config --clear
# Clear a specific profile
bubble-io-cli config --clear --profile staging
# Clear ALL profiles
bubble-io-cli config --clear --all| Option | Alias | Description |
|---|---|---|
| --app <name> | -a | Your Bubble app subdomain |
| --key <apiKey> | -k | Your private Bubble API key |
| --profile <name> | -p | Named profile to save/load |
| --show | | Display the current config |
| --list | | List all profiles |
| --use <profile> | | Switch active profile |
| --clear | | Clear current or named profile |
| --all | | Combined with --clear: remove all profiles |
backup — Export Data
Download records from any Bubble data type and save them locally. Supports JSON and CSV output, server-side filtering, incremental exports, cloud upload, encryption, watch mode, and CI-friendly JSON output.
# Basic backup (test environment, all records)
bubble-io-cli backup --type Product
# Backup from production
bubble-io-cli backup --type User --env version-live
# Limit to first 100 records
bubble-io-cli backup --type Product --limit 100
# Export as CSV
bubble-io-cli backup --type Order --format csv
# Server-side filtering (Bubble constraints)
bubble-io-cli backup --type Order \
--constraint '[{"key":"status","constraint_type":"equals","value":"active"}]'
# Incremental export — only records modified since a date
bubble-io-cli backup --type User --since 2026-08-01
# Watch mode — backup every hour automatically
bubble-io-cli backup --type Product --watch --interval 3600
# Upload to Amazon S3 after export
bubble-io-cli backup --type User --destination s3://my-bucket/backups
# Upload to Google Cloud Storage
bubble-io-cli backup --type User --destination gs://my-bucket/backups
# Encrypt the backup (AES-256-GCM)
export BUBBLE_BACKUP_PASSPHRASE="your-strong-passphrase"
bubble-io-cli backup --type Product --encrypt
# Send Slack / Discord notification on completion
bubble-io-cli backup --type Product \
--notify-slack https://hooks.slack.com/services/T/B/secret \
--notify-discord https://discord.com/api/webhooks/123/secret
# Machine-readable JSON output for CI/CD
bubble-io-cli backup --type Product --json| Option | Alias | Description | Default |
|---|---|---|---|
| --type <datatype> | -t | Bubble data type (required) | — |
| --env <environment> | -e | version-test or version-live | version-test |
| --output <dir> | -o | Output directory | . |
| --limit <number> | -l | Max records to fetch | all |
| --format <type> | -f | json or csv | json |
| --constraint <json> | -c | Bubble constraint JSON array | — |
| --since <date> | | Export only records modified after date | — |
| --watch | | Continuous backup mode | — |
| --interval <seconds> | | Watch interval (min 10s) | 3600 |
| --destination <url> | | Cloud upload: s3:// or gs:// | — |
| --encrypt | | AES-256-GCM encryption | — |
| --notify-slack <url> | | Slack Incoming Webhook URL | — |
| --notify-discord <url> | | Discord Webhook URL | — |
| --notify-on-error | | Also notify on failures | — |
| --json | | Machine-readable JSON output | — |
Cloud upload: Requires
npm install @aws-sdk/client-s3(for S3) ornpm install @google-cloud/storage(for GCS), loaded lazily.Encryption: Reads passphrase from
BUBBLE_BACKUP_PASSPHRASEenv var. Output file uses.encextension.
restore — Upload Records to Bubble
Bulk-upload records from a local backup file back to Bubble via the Data API.
# Restore to test environment
bubble-io-cli restore --file ./backup-product-2026-08-07.json
# Restore with upsert mode (create new + update existing by _id)
bubble-io-cli restore --file ./backup-user.json --mode upsert
# Preview what would be restored (no API calls)
bubble-io-cli restore --file ./backup-product.json --dry-run
# Control parallelism
bubble-io-cli restore --file ./backup-order.json --concurrency 10| Option | Alias | Description | Default |
|---|---|---|---|
| --file <path> | -f | Backup JSON file (required) | — |
| --env <environment> | -e | Target environment | version-test |
| --type <datatype> | -t | Override data type from file | — |
| --mode <mode> | -m | create or upsert | create |
| --concurrency <n> | | Parallel API requests (1–20) | 5 |
| --dry-run | | Simulate without API calls | — |
diff — Compare Data
Compare live Bubble data against a local backup file and show exactly what changed.
# Compare Product type with a local backup (full table fetch)
bubble-io-cli diff --file ./backup-product-2026-08-07.json
# ⚡ Fast mode: only fetch the specific record IDs from the backup
# Best for large tables — no full table scan, no extra Capacity Units consumed
bubble-io-cli diff --file ./backup-user.json --local-only
# Limit the number of remote records fetched (useful for spot-checking)
bubble-io-cli diff --file ./backup-product.json --limit 500
# Compare specific fields only
bubble-io-cli diff --file ./backup-user.json --fields name,email,plan
# Show summary counts only (no per-record details)
bubble-io-cli diff --file ./backup-order.json --summary
# Combine --local-only with --summary for a quick health check
bubble-io-cli diff --file ./backup-user.json --local-only --summary| Option | Alias | Description | Default |
|---|---|---|---|
| --file <path> | -f | Local backup file (required) | — |
| --type <datatype> | -t | Override the data type | — |
| --env <environment> | -e | Target environment | version-test |
| --fields <list> | | Comma-separated fields to compare | all |
| --limit <number> | -l | Cap the number of records fetched from Bubble | all |
| --local-only | | Only fetch the specific IDs from the backup — much faster for large tables | — |
| --summary | | Show counts only, no per-record detail | — |
--local-onlytrade-off: This mode queries Bubble only for the record IDs already present in the backup file (in chunks of 50), making it extremely fast and capacity-efficient. However, it cannot detect records that were added to Bubble after the backup was taken. Use the default (full fetch) or--limitwhen you need to detect new records too.
--local-onlyand--limitare mutually exclusive — the CLI will exit with an error if both are specified together.
health — Check API Connectivity
Verify that your credentials are valid and your Bubble app is reachable.
# Check test environment
bubble-io-cli health
# Check both environments at once
bubble-io-cli health --all
# Check production
bubble-io-cli health --env version-live
# Machine-readable output for CI
bubble-io-cli health --json| Option | Alias | Description | Default |
|---|---|---|---|
| --env <environment> | -e | Environment to check | version-test |
| --all | | Test both environments | — |
| --type <datatype> | -t | Data type to ping | User |
| --json | | JSON output | — |
schema list — Inspect App Schema
List all data types and their field definitions using the Bubble Meta API.
Requires: Enable "Expose Data API" and "Expose schema" in Bubble → Settings → API.
# List all data types
bubble-io-cli schema list
# Show fields for all types
bubble-io-cli schema list --fields
# Inspect a specific type
bubble-io-cli schema list --type Product
# Export schema as JSON
bubble-io-cli schema list --jsonschema diff — Compare Schema Between Environments
# Compare test vs live schema (default)
bubble-io-cli schema diff
# Custom environments
bubble-io-cli schema diff --env-a version-test --env-b version-live
# JSON output for CI comparison
bubble-io-cli schema diff --jsonOutput is color-coded: + green (added), - red (removed), ~ yellow (changed), at the field level.
schema erd — Generate Entity-Relationship Diagram
Generate a Mermaid.js Entity-Relationship Diagram from your Bubble schema. It automatically detects relationships between your data types.
# Print Mermaid ERD to the terminal
bubble-io-cli schema erd
# Save directly to a markdown file (renders in GitHub/VS Code)
bubble-io-cli schema erd --output ./erd.md
# Include Bubble built-in types (User, FileObject, etc.)
bubble-io-cli schema erd --include-system-types
# Print raw Mermaid code block only (useful for piping)
bubble-io-cli schema erd --rawschema migrate:generate — Schema-as-Code Database Migrations (New in v4.4.0)
Capture, track, and version control your Bubble.io database schema evolution declaratively.
- Generates timestamped JSON migration files in
./migrations/<timestamp>_<name>.json - Supports 5 discrete declarative operations:
ADD_TABLE,REMOVE_TABLE,ADD_FIELD,REMOVE_FIELD, andCHANGE_FIELD_TYPE - Tracks the local baseline state using
migrations/schema.lock.json - Supports direct environment diffing between
version-liveandversion-test(--from-env)
# Initialize baseline schema snapshot (creates migrations/schema.lock.json)
bubble-io-cli schema migrate:generate --snapshot-only
# Generate a migration tracking changes against the local lockfile baseline
bubble-io-cli schema migrate:generate --name add_phone_and_status --description "Add phone and status fields to User"
# Compare test directly against production without modifying the local lockfile
bubble-io-cli schema migrate:generate --name sync_prod_to_test --from-env version-live --env version-test
# Specify custom migrations directory
bubble-io-cli schema migrate:generate --name add_orders --dir ./custom-migrations
# Output migration result as JSON for CI/CD automation
bubble-io-cli schema migrate:generate --name ci_check --jsonMigration File Example (migrations/20260808224500_add_phone_and_status.json):
{
"version": "20260808224500",
"name": "add_phone_and_status",
"description": "Add phone and status fields to User",
"createdAt": "2026-08-08T22:45:00.000Z",
"app": "my-cool-app",
"environment": "version-test",
"changes": [
{
"action": "ADD_FIELD",
"table": "User",
"field": "phone_number",
"type": "text"
},
{
"action": "CHANGE_FIELD_TYPE",
"table": "Product",
"field": "price",
"previousType": "text",
"type": "number"
}
]
}schema migrate:list — Inspect Migration History (New in v4.4.0)
Display all recorded migration files and inspect the current schema lockfile baseline:
# View formatted terminal table of migrations
bubble-io-cli schema migrate:list
# Custom directory
bubble-io-cli schema migrate:list --dir ./custom-migrations
# Machine-readable JSON output
bubble-io-cli schema migrate:list --jsonworkflow trigger — Trigger Backend Workflows
Call Bubble backend workflows that have "This workflow can be triggered by API" enabled.
# Trigger a workflow
bubble-io-cli workflow trigger --name send-invoice
# Trigger with inline parameters
bubble-io-cli workflow trigger --name process-order --data '{"orderId":"abc123"}'
# Trigger with a complex payload from a file (recommended for Windows)
bubble-io-cli workflow trigger --name process-order --data @payload.json
# Trigger on production
bubble-io-cli workflow trigger --name daily-report --env version-live
# JSON output for scripting
bubble-io-cli workflow trigger --name send-invoice --json| Option | Alias | Description | Default |
|---|---|---|---|
| --name <workflowName> | -n | API name of the workflow (required) | — |
| --env <environment> | -e | Target environment | version-live |
| --data <json> | -d | JSON object of workflow parameters | — |
| --json | | Machine-readable output | — |
seed — Populate Test Data
Bulk-create records in Bubble from a local JSON fixture file. Ideal for seeding test environments.
# Seed from a fixture file
bubble-io-cli seed --file ./seeds/products.json
# Preview without creating anything
bubble-io-cli seed --file ./seeds/users.json --dry-run
# Control parallelism
bubble-io-cli seed --file ./seeds/orders.json --concurrency 10Seed file format:
{
"type": "Product",
"records": [
{ "name": "Widget", "price": 9.99 },
{ "name": "Gadget", "price": 24.99 }
]
}mock — Local Mock Server
Start a local Express HTTP server that exposes a Bubble-compatible Data API from a backup JSON file. Perfect for offline development and integration testing.
# Start mock server on default port 3333
bubble-io-cli mock --file ./backup-product.json
# Custom port
bubble-io-cli mock --file ./backup-user.json --port 4000
# Enable CORS (for browser-based testing)
bubble-io-cli mock --file ./backup-product.json --cors
# Load multiple data types at once
bubble-io-cli mock --file Product=./backup-product.json --file User=./backup-user.jsonAvailable endpoints once server is running:
| Method | Path | Description |
|---|---|---|
| GET | /api/1.1/obj/:type?cursor=0&limit=100 | Paginated list |
| GET | /api/1.1/obj/:type/:id | Single record |
| POST | /api/1.1/obj/:type | Create (in-memory) |
| PATCH | /api/1.1/obj/:type/:id | Update (in-memory) |
| DELETE | /api/1.1/obj/:type/:id | Delete (in-memory) |
| GET | /health | Server status and loaded types |
plugin — Plugin Editor API
Manage Bubble plugins via the Plugin Editor API.
Requires:
BUBBLE_PLUGIN_TOKENenvironment variable.
Get your token: Bubble Editor → Plugins → Plugin Editor → Settings → API token
export BUBBLE_PLUGIN_TOKEN="your-plugin-editor-token"
# List all plugins
bubble-io-cli plugin list
# Get a specific plugin's full definition
bubble-io-cli plugin get <pluginId>
# Deploy a plugin definition (create new)
bubble-io-cli plugin deploy --file ./plugin.json
# Update an existing plugin
bubble-io-cli plugin deploy --file ./plugin.json --id existing-plugin-id
# Preview without making API calls
bubble-io-cli plugin deploy --file ./plugin.json --dry-rungenerate — Scaffold Templates & TypeScript Types
Generate boilerplate TypeScript files for common Bubble integration patterns, or automatically create TypeScript interface definitions from your live Bubble schema.
generate types — TypeScript Interface Generator (New in v3.0.0)
Connects to the Bubble Meta API and generates clean, fully-typed TypeScript interfaces for your data types — perfect for building type-safe integrations and SDKs.
# Preview all interfaces to stdout
bubble-io-cli generate types
# Export all data types to a single declaration file
bubble-io-cli generate types --output ./src/bubble-types.d.ts
# Export only a single data type
bubble-io-cli generate types --type Product --output ./src/types/product.d.ts
# Target the production environment
bubble-io-cli generate types --env version-live --output ./src/bubble-types.d.ts
# Use a named credential profile
bubble-io-cli generate types --profile staging --output ./src/bubble-types.d.tsExample output for a Product type with mixed fields:
/**
* Auto-generated by bubble-io-cli v3.0.0
* App: my-cool-app | Environment: version-test
* Generated: 2026-08-07T22:00:00.000Z
*
* DO NOT EDIT — regenerate with: bubble-io-cli generate types
*/
export interface Product {
/** Unique Bubble record identifier */
_id: string;
/** ISO 8601 creation timestamp */
'Creation Date': string;
/** ISO 8601 last modification timestamp */
'Modified Date': string;
/** [text] */
Name?: string;
/** [number] */
Price?: number;
/** [boolean] */
'Is Active'?: boolean;
/** [date] */
'Launch Date'?: string;
/** [Category] relationship → stored as Bubble ID */
Category?: string;
/** [list of text] */
Tags?: string[];
}Bubble → TypeScript type mapping:
| Bubble type | TypeScript type | Notes |
|---|---|---|
| text | string | |
| number | number | |
| boolean | boolean | |
| date | string | ISO 8601 string (Bubble returns dates as strings) |
| geographic address | BubbleGeographicAddress | Helper interface auto-emitted |
| file, image, option | string | URL or string value |
| list of text/number/... | string[], number[], ... | |
| list of <CustomType> | string[] | Bubble stores lists as ID arrays |
| Custom data type | string | Relationship → stored as Bubble ID |
| Option | Alias | Description | Default |
|---|---|---|---|
| --env <environment> | -e | version-test or version-live | version-test |
| --profile <name> | -p | Named profile to use | active profile |
| --type <name> | -t | Generate only this data type | all types |
| --output <file> | -o | Write to file (omit for stdout) | stdout |
generate — Scaffold Integration Templates
# List available templates
bubble-io-cli generate --list
# Scaffold a plugin server-side action
bubble-io-cli generate --template plugin-action --name SendEmail
# Scaffold a CRUD API connector for a data type
bubble-io-cli generate --template api-connector --name Product
# Scaffold a webhook receiver for Bubble data triggers
bubble-io-cli generate --template data-trigger --name OrderCreated --output ./webhooks| Template | Description |
|---|---|
| plugin-action | Typed Bubble plugin server-side action scaffold |
| api-connector | Full CRUD connector class for a Bubble data type |
| data-trigger | HTTP webhook receiver for Bubble data change events |
generate ci — CI/CD Pipeline Generator (New in v4.0.0, Enhanced in v4.5.0)
Generate a production-ready GitHub Actions or GitLab CI workflow file that automatically backs up your Bubble data every night. One command, zero manual YAML editing.
# Generate with all defaults (backs up User type, runs at 03:00 UTC, 30-day retention)
bubble-io-cli generate ci --provider github
# Generate for multiple types (uses matrix strategy to run in parallel)
bubble-io-cli generate ci --provider github --type User,Product,Order
# Generate a GitLab CI pipeline
bubble-io-cli generate ci --provider gitlab --type User,Product
# Customize for a specific type, schedule, and retention period
bubble-io-cli generate ci --provider github \
--type User \
--env version-live \
--cron "0 1 * * *" \
--retention 14 \
--format json
# Preview what would be generated without writing to disk
bubble-io-cli generate ci --provider gitlab --type Product --format csv --dry-run --cli-version 4.0.0 \
--output .github/workflows
# Export to CSV instead of JSON
bubble-io-cli generate ci --provider github --type Product --format csvWhat the generated workflow does:
- ✅ Runs on a nightly cron schedule (configurable)
- ✅ Supports
workflow_dispatchfor manual on-demand runs - ✅ Installs
bubble-io-clifrom npm (latest or pinned version) - ✅ Configures credentials securely from GitHub Secrets
- ✅ Runs
bubble-io-cli backup --jsonand validates the result - ✅ Uploads the backup as a GitHub Actions Artifact (configurable retention)
- ✅ Writes a rich summary table to the GitHub Actions UI
Required GitHub Secrets (add under Settings → Secrets → Actions):
| Secret | Description |
|---|---|
| BUBBLE_APP_NAME | Your Bubble app subdomain (e.g. my-app) |
| BUBBLE_API_KEY | Your Bubble private Data API key |
| Option | Alias | Description | Default |
|---|---|---|---|
| --provider <name> | | CI/CD provider: github | required |
| --type <datatype> | -t | Bubble data type to back up | User |
| --env <environment> | -e | Bubble environment | version-live |
| --cron <expression> | | UTC cron schedule expression | 0 3 * * * |
| --retention <days> | | Artifact retention days (1–90) | 30 |
| --format <type> | -f | Backup format: json or csv | json |
| --cli-version <version> | | npm version to pin | latest |
| --output <dir> | -o | Output directory | .github/workflows |
query — Interactive REPL (New in v3.1.0)
Start a fully interactive terminal session to search, filter, and browse Bubble records in real time — without leaving the CLI.
# Start interactive query session (test environment)
bubble-io-cli query
# Target the live environment
bubble-io-cli query --env version-live
# Use a named profile with custom page size
bubble-io-cli query --profile staging --page-size 10Interactive session flow:
🫧 bubble-io-cli Interactive Query
App: my-cool-app | Env: version-test
Select a data type:
1) User
2) Product
3) Order
> 2
[Product] Quick options:
f) Add / change text search
c) Add / change field constraint
x) Clear all filters
t) Change data type
q) Quit
Enter) Fetch records (current filters)
> f
Search in "Name": widget
┌──────────────────────────┬──────────────┬───────┬──────────────────────────────┐
│ _id │ Name │ Price │ Creation Date │
├──────────────────────────┼──────────────┼───────┼──────────────────────────────┤
│ 1723031234567x123456789 │ Widget │ 9.99 │ 2026-08-01T10:00:00.000Z │
│ 1723031234568x987654321 │ Widget Pro │ 19.99 │ 2026-08-02T11:00:00.000Z │
└──────────────────────────┴──────────────┴───────┴──────────────────────────────┘
Showing 2 records | Page 1 of 1 | Total: 2
Actions:
r) Refine / change filters
t) Change data type
e) Export current page to JSON
q) Quit
> e
✅ Exported 2 record(s) → query-export-Product-2026-08-07T22-00-00-000Z.jsonSupported constraint operators: equals, not equal, text contains, greater than, less than, is_empty, is_not_empty
| Option | Alias | Description | Default |
|---|---|---|---|
| --env <environment> | -e | version-test or version-live | version-test |
| --profile <name> | -p | Named credential profile | active profile |
| --page-size <n> | | Records per page (max: 100) | 20 |
Tip: Use
Ctrl+Cat any time to exit the session gracefully.
audit privacy — PII & Privacy Security Audit (New in v3.2.0)
Scan your Bubble schema or a local backup file for potentially exposed Personally Identifiable Information (PII) and security risks. The scanner detects high-risk field names across 8 categories and outputs a color-coded report with Bubble Privacy Rule recommendations.
# Scan your live remote schema (requires credentials)
bubble-io-cli audit privacy
# Scan a local backup JSON file
bubble-io-cli audit privacy --file ./backup-user-2026-08-07.json
# Scan only a specific data type
bubble-io-cli audit privacy --type User
# Only show CRITICAL and HIGH findings
bubble-io-cli audit privacy --min-risk HIGH
# Target the production environment
bubble-io-cli audit privacy --env version-live
# JSON output for CI pipelines
bubble-io-cli audit privacy --jsonExample terminal output:
🔍 Privacy Audit Report — my-app [version-test]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
source: remote schema scanned: 8 types · 54 fields
Findings: 2 CRITICAL · 3 HIGH · 1 MEDIUM
──────────────────────────────────────────────────────────
🔴 CRITICAL — User.password_hash [text]
⚠ Field name matches credential pattern ("password"). Exposing this field risks account takeover.
💡 In Bubble Privacy Rules: set this field to "No one" access. Never expose credentials via the Data API.
🔴 CRITICAL — User.api_token [text]
⚠ Field name matches credential pattern ("token"). Exposing this field risks account takeover.
💡 In Bubble Privacy Rules: set this field to "No one" access.
🟠 HIGH — User.email [text]
⚠ Field name matches personal contact information ("email"). PII exposure risk.
💡 In Bubble Privacy Rules: restrict to "This User" and explicitly granted roles only.
📋 Next Steps:
1. Open Bubble Editor → Data → Privacy
2. For each CRITICAL finding — set the field to "No one" access
3. For each HIGH finding — restrict to authenticated users or "This User"Detection categories:
| Risk | Category | Example field names |
|---|---|---|
| 🔴 CRITICAL | Credentials | password, token, api_key, secret, auth_token |
| 🔴 CRITICAL | Financial | credit_card, ssn, iban, bank_account, cvv |
| 🟠 HIGH | Government ID | passport, national_id, driver_license |
| 🟠 HIGH | Biometric | fingerprint, face_id, biometric |
| 🟠 HIGH | Contact PII | email, phone, address, date_of_birth |
| 🟠 HIGH | Medical | diagnosis, medical, patient, prescription |
| 🟡 MEDIUM | Geolocation | gps, latitude, longitude, coordinates |
| 🟡 MEDIUM | Demographics | full_name, salary, gender, ethnicity |
CI Integration: The command exits with code
1when CRITICAL findings are detected — use it as a security gate in GitHub Actions.
| Option | Description | Default |
|---|---|---|
| --file <path> | Scan a local backup JSON file | — |
| --env <env> | Target Bubble environment | version-test |
| --type <name> | Scan only a specific data type | all types |
| --min-risk <level> | Minimum severity: MEDIUM, HIGH, CRITICAL | MEDIUM |
| --json | Machine-readable JSON output | false |
| --profile <name> | Named credential profile | active |
export db — Export to Database (New in v4.0.0)
Export records from any Bubble data type directly into an external database. Supports SQLite (zero-config, local), PostgreSQL, and BigQuery (enterprise). Uses a provider pattern — heavy database SDKs are loaded lazily, so installing the full CLI stays lean.
# ── SQLite — zero config, local file, no extra dependencies ──────────────────
bubble-io-cli export db --type User --target sqlite --db ./bubble.db
# Export a specific type to a named file
bubble-io-cli export db --type Product --target sqlite --db ./products.db
# Limit the number of records exported
bubble-io-cli export db --type Order --target sqlite --db ./orders.db --limit 500
# ── PostgreSQL — requires: npm install pg ─────────────────────────────────────
bubble-io-cli export db --type User \
--target postgres \
--connection-string "postgresql://user:pass@localhost:5432/mydb"
# Export from live environment
bubble-io-cli export db --type Product \
--target postgres \
--env version-live \
--connection-string "postgresql://user:[email protected]/prod"
# ── BigQuery — requires: npm install @google-cloud/bigquery ──────────────────
# Uses Application Default Credentials (gcloud auth application-default login)
bubble-io-cli export db --type Order \
--target bigquery \
--project my-gcp-project \
--dataset bubble_data
# With a service account key file
bubble-io-cli export db --type User \
--target bigquery \
--project my-gcp-project \
--dataset bubble_data \
--key-file ./service-account.jsonHow it works:
- Fetches all records from Bubble via the Data API (with cursor pagination)
- Connects to the target database
- Creates the table automatically if it doesn't exist (schema inferred from the first record)
- Adds any new columns that appear in subsequent records (
ALTER TABLE) - Upserts all records idempotently — safe to re-run, no duplicates
Optional dependencies (installed only when needed):
| Target | Install command | Notes |
|---|---|---|
| sqlite | (included — uses sql.js) | Pure JavaScript, no native compilation |
| postgres | npm install pg | Also: npm install -D @types/pg for TypeScript |
| bigquery | npm install @google-cloud/bigquery | Uses ADC or --key-file |
SQLite — what gets created:
A .db file with one table per Bubble data type. Table name = sanitized type name (e.g. User → user). All fields are stored with inferred types (TEXT, REAL, INTEGER). JSON objects/arrays are stored as JSON strings.
BigQuery — table naming convention:
Table names are prefixed with bubble_ (e.g. Order → bubble_order) to avoid conflicts with existing BQ tables. Streaming inserts use _id as insertId for best-effort deduplication.
| Option | Alias | Description | Default |
|---|---|---|---|
| --type <datatype> | -t | Bubble data type to export (required) | — |
| --target <provider> | | sqlite | postgres | bigquery (required) | — |
| --env <environment> | -e | Bubble environment | version-test |
| --limit <number> | -l | Maximum records to export | all |
| --profile <name> | -p | Named credential profile | active |
| --db <path> | | SQLite .db file path | ./bubble.db |
| --connection-string <url> | | PostgreSQL connection string | — |
| --project <id> | | GCP project ID (BigQuery) | — |
| --dataset <id> | | BigQuery dataset ID | bubble_data |
| --key-file <path> | | Service account key JSON file | — |
seed — Relational Data Import (New in v4.1.0)
Import records into Bubble from a local JSON file. Supports two formats:
Legacy Format (single data type)
bubble-io-cli seed --file seeds/products.json
bubble-io-cli seed --file seeds/users.json --type User --env version-test
bubble-io-cli seed --file seeds/orders.json --dry-runLegacy seed file structure:
{
"type": "Product",
"records": [
{ "Name": "Widget Pro", "Price": 29.99 },
{ "Name": "Widget Lite", "Price": 9.99 }
]
}Relational Format (multi-type with cross-references)
Import entire interconnected datasets in a single command. Use _ref aliases to define cross-links between records — the CLI handles creation order automatically via a dependency graph engine.
bubble-io-cli seed --file seeds/catalog.json
bubble-io-cli seed --file seeds/catalog.json --dry-run # preview the execution plan
bubble-io-cli seed --file seeds/catalog.json --json # machine-readable outputRelational seed file structure:
{
"Category": [
{ "_ref": "@cat_tech", "Name": "Technology" },
{ "_ref": "@cat_laptops", "Name": "Laptops", "Parent": "@cat_tech" }
],
"Product": [
{
"_ref": "@prod_macbook",
"Name": "MacBook Pro M3",
"Category": "@cat_laptops",
"Available_Sizes": ["@size_14", "@size_16"]
}
],
"Size": [
{ "_ref": "@size_14", "Name": "14 inch", "Product": "@prod_macbook" },
{ "_ref": "@size_16", "Name": "16 inch", "Product": "@prod_macbook" }
],
"Price": [
{ "Amount": 1999, "Currency": "USD", "Product": "@prod_macbook", "Size": "@size_14" },
{ "Amount": 2499, "Currency": "USD", "Product": "@prod_macbook", "Size": "@size_16" }
]
}How the _ref / @alias system works:
_refassigns a temporary local alias to a record (e.g."_ref": "@prod_macbook"). It is never sent to Bubble.- Any field value starting with
@is treated as a cross-reference and replaced at runtime with the real Bubble_idof the aliased record. - Arrays of references are fully supported:
"Sizes": ["@size_14", "@size_16"].
Graph resolution capabilities:
| Scenario | Supported |
|---|---|
| N-level deep dependencies (A→B→C→…→N) | ✅ Unlimited depth |
| Array / List of references | ✅ ["@ref1", "@ref2"] |
| Self-referencing hierarchies (e.g. Category tree) | ✅ Auto-detected |
| Circular dependencies (A↔B) | ✅ 2-pass: Create + deferred PATCH |
| Unknown @ref alias | ✅ Fails fast with clear error before any API calls |
| Duplicate _ref aliases | ✅ Fails fast with clear error before any API calls |
| Atomic rollback on error | ✅ Reverse cleanup with --rollback-on-error |
| Live schema validation | ✅ Type + field + relational type-check with --check-schema |
Tip: Always run with
--dry-runfirst to preview the full creation order. Use--check-schemato validate types and fields against the live Bubble schema before any data is written. Combine both for a zero-risk dry validation:bubble-io-cli seed --file catalog.json --check-schema --dry-run
Options:
| Option | Short | Description | Default |
|---|---|---|---|
| --file <path> | -f | Path to seed JSON file (required) | — |
| --type <datatype> | -t | Override data type (legacy format only) | from file |
| --env <env> | -e | Target environment | version-test |
| --check-schema | | Validate all types and fields against the live Bubble schema before seeding | false |
| --rollback-on-error | | Automatically delete created records in reverse order if any error occurs | false |
| --concurrency <n> | | Parallel requests (legacy mode only, 1–20) | 5 |
| --dry-run | | Preview execution plan without API calls | false |
| --json | | Machine-readable JSON output | false |
| --profile <name> | -p | Named credential profile | active |
--check-schema validation rules:
| Check | Severity | Description |
|---|---|---|
| Missing Type | ❌ Error | Type in seed does not exist in Bubble — aborts seed |
| Missing Field | ❌ Error | Field in seed does not exist on the Bubble type — aborts seed |
| Wrong Thing link | ⚠ Warning | A @ref value points to type A but the Bubble field expects type B |
| Wrong list of Thing | ⚠ Warning | An array of @refs points to type A but Bubble field is list of B |
| Number → text field | ⚠ Warning | A JS number is sent to a Bubble text field |
| Boolean → text field | ⚠ Warning | A JS boolean is sent to a Bubble text field |
completions — Shell Tab Completion
Generate tab-completion scripts for Bash, Zsh, or Fish.
# Bash (add to ~/.bashrc)
source <(bubble-io-cli completions --bash)
# Zsh (add to ~/.zshrc)
source <(bubble-io-cli completions --zsh)
# Fish (run once)
bubble-io-cli completions --fish > ~/.config/fish/completions/bubble-io-cli.fish🏗️ Architecture
bubble-io-cli/
├── src/
│ ├── index.ts # CLI entry point — registers all commands
│ ├── cli/ # Interactive Terminal UI (TUI)
│ │ └── interactive.ts # @clack/prompts interactive menu and sub-menu flows
│ ├── commands/ # Command definitions (CLI Layer)
│ │ ├── config.ts # config — credential storage
│ │ ├── backup.ts # backup — export records
│ │ ├── restore.ts # restore — upload records
│ │ ├── diff.ts # diff — compare local vs remote
│ │ ├── health.ts # health — ping API
│ │ ├── schema.ts # schema list / diff / erd / migrate:generate / migrate:list
│ │ ├── workflow.ts # workflow trigger — backend workflows
│ │ ├── seed.ts # seed — relational import & graph execution
│ │ ├── mock.ts # mock — local API server
│ │ ├── plugin.ts # plugin list / get / deploy
│ │ ├── generate.ts # generate (templates) / generate types / generate ci
│ │ ├── export.ts # export db — database export (SQLite/PostgreSQL/BigQuery)
│ │ ├── audit.ts # audit privacy — PII scanner
│ │ └── completions.ts # completions — shell tab completion
│ ├── services/ # Business logic
│ │ ├── bubble-api.ts # BubbleApiClient — Data API (CRUD + pagination)
│ │ ├── bubble-meta.ts # BubbleMetaClient — Meta API (schema)
│ │ ├── bubble-plugin.ts # BubblePluginClient — Plugin Editor API
│ │ └── db-providers/ # Database export providers (provider pattern)
│ │ ├── index.ts # DbProvider interface + getDbProvider() factory
│ │ ├── sqlite.ts # SQLite provider (sql.js, zero native deps)
│ │ ├── postgres.ts # PostgreSQL provider (dynamic import: pg)
│ │ └── bigquery.ts # BigQuery provider (dynamic import: @google-cloud/bigquery)
│ └── utils/ # Infrastructure helpers
│ ├── storage.ts # Configstore — config & multi-profile
│ ├── csv.ts # CSV serialization (flattenRecord + jsonToCsv)
│ ├── encryption.ts # AES-256-GCM encrypt/decrypt
│ ├── cloud-upload.ts # S3 + GCS upload adapters
│ ├── notifications.ts # Slack + Discord webhook dispatcher
│ ├── pii-scanner.ts # PII detection engine
│ ├── plugin-loader.ts # Plugin auto-discovery & registration
│ ├── plugin-manager.ts # Lifecycle hook registry (PluginManager singleton)
│ ├── graph-resolver.ts # DAG builder, topological sort, circular dep detection
│ ├── relational-seeder.ts # Sequential execution engine for relational imports
│ ├── schema-preflight.ts # Schema pre-flight validator for seed files
│ ├── schema-diff.ts # Schema diffing engine
│ ├── schema-migrations.ts # Schema-as-Code & migration engine
│ ├── schema-erd.ts # Mermaid ERD generator
│ ├── type-generator.ts # TypeScript interface generator
│ ├── table-renderer.ts # cli-table3 table renderer
│ ├── query-session.ts # REPL session state machine
│ └── ci-generators/
│ └── github-actions.ts # GitHub Actions YAML generator
│
└── tests/ # Vitest unit tests (321 tests)
├── plugin-manager.test.ts
├── schema-migrations.test.ts
├── interactive.test.ts
├── schema-preflight.test.ts
├── relational-seeder.test.ts
├── type-generator.test.ts
├── pii-scanner.test.ts
└── ...Separation of concerns: Command files handle only UX (spinners, colors, exit codes). All business logic lives in
services/andutils/.
🌍 Environment Variables
| Variable | Used by | Description |
|---|---|---|
| BUBBLE_BACKUP_PASSPHRASE | backup --encrypt | Passphrase for AES-256-GCM backup encryption |
| BUBBLE_PLUGIN_TOKEN | plugin commands | Bubble Plugin Editor API token |
| AWS_ACCESS_KEY_ID | backup --destination s3:// | AWS credentials for S3 upload |
| AWS_SECRET_ACCESS_KEY | backup --destination s3:// | AWS credentials for S3 upload |
| AWS_REGION | backup --destination s3:// | AWS region for S3 upload |
| GOOGLE_APPLICATION_CREDENTIALS | backup --destination gs:// | GCP service account JSON path |
| S3_BACKUP_BUCKET | plugin-s3-backup example | Target S3 bucket for auto-upload plugin |
| SLACK_WEBHOOK_URL | plugin-slack-notifier example | Slack Incoming Webhook URL for notifications plugin |
🛠️ Development
# Clone the repository
git clone https://github.com/alexandrmotologa/bubble-io-cli.git
cd bubble-io-cli
# Install dependencies
npm install
# Run in development mode (tsx, no build step)
npm run dev -- config --show
# Build the production bundle
npm run build
# Run tests
npm test
# Type check only
npm run lint🔌 Plugin Extensibility (v4.5.0+)
bubble-io-cli supports a powerful plugin system that lets you extend the CLI in two ways:
- Custom Commands — Add entirely new commands to the CLI via
register(program) - Lifecycle Hooks — React to events from existing commands without modifying core code
Available Lifecycle Hooks
| Hook | Fires when |
|---|---|
| init | CLI starts, after all plugins load |
| beforeBackup / afterBackup | Before/after backup command |
| beforeRestore / afterRestore | Before/after restore command |
| onSchemaFetch | After schema list / schema diff / schema erd |
| onSchemaDiff | After diff is computed in schema diff |
| beforeExport / afterExport | Before/after export db command |
Install a Community Plugin
# Install any npm package named bubble-io-cli-plugin-*
npm install -g bubble-io-cli-plugin-example
# The plugin is automatically discovered on next CLI run
bubble-io-cli plugin ext listWrite a Hook Plugin in 60 Seconds
mkdir -p ~/.bubble-cli/pluginsCreate ~/.bubble-cli/plugins/my-logger.js:
module.exports = {
name: 'my-logger',
version: '1.0.0',
description: 'Logs backup events to the console',
register(_program) {}, // no custom commands needed
hooks: {
afterBackup(ctx) {
console.log(`[my-logger] ✅ ${ctx.records} records backed up → ${ctx.filePath}`);
},
onSchemaDiff(ctx) {
if (!ctx.diff.identical) {
console.warn(`[my-logger] ⚠️ Schema changed between ${ctx.envA} and ${ctx.envB}`);
}
},
},
};Write a Custom Command Plugin
Create ~/.bubble-cli/plugins/hello.js:
module.exports = {
name: 'hello',
version: '1.0.0',
description: 'My first plugin',
register(program) {
program.command('hello').description('Say hello!').action(() => {
console.log('Hello from my plugin! 🎉');
});
},
};bubble-io-cli hello
# → Hello from my plugin! 🎉Manage Plugins
bubble-io-cli plugin ext list # List all loaded plugins
bubble-io-cli plugin ext info <name> # Show plugin details
bubble-io-cli plugin ext reload # Force plugin re-discoveryReady-to-use Examples
Check out the fully functional example plugins in the examples/ directory:
examples/plugin-s3-backup/— Auto-uploads every backup file to an S3 bucket viaafterBackupexamples/plugin-slack-notifier/— Sends rich Slack notifications for backup, restore, schema diff, and export events
Write Your Own Plugin
→ See the full Plugin Authoring Guide for the complete interface spec, all hook context types, TypeScript template, publishing guide, Commander patterns, and more examples.
🤝 Contributing
Contributions are warmly welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feat/my-new-command - Commit your changes:
git commit -m 'feat: add my-new-command' - Push to the branch:
git push origin feat/my-new-command - Open a Pull Request
Architecture guide: new commands go in src/commands/, new API logic in src/services/, utility helpers in src/utils/. See docs/architecture.md for the full design.
📝 License
Distributed under the MIT License. See LICENSE for more information.
