metabase-iac
v0.3.0
Published
Infrastructure as Code for Metabase — manage collections, questions, dashboards as code
Maintainers
Readme
metabase-iac
Infrastructure as Code for Metabase. Manage collections, questions (SQL, MongoDB, structured), dashboards, and text cards as code.
Similar to how Grafana dashboards can be managed as JSON, metabase-iac lets you version-control your Metabase resources and apply changes through a CLI.
Install
pnpm add metabase-iac
# or
npm install metabase-iacQuick start
# 1. Create a new directory for your Metabase instance
mkdir metabase-prod && cd metabase-prod
pnpm init && pnpm add metabase-iac
# 2. Configure credentials
cat > .env <<EOF
METABASE_URL=https://your-metabase.example.com
[email protected]
METABASE_PASSWORD=your-password
EOF
# 3. Pull all existing resources from Metabase
npx metabase-iac pull
# 4. You now have collections/, questions/, dashboards/ — commit them to git
git init && git add -A && git commit -m "Initial pull from Metabase"Commands
| Command | Description |
|---|---|
| metabase-iac pull | Import collections, questions, and dashboards from Metabase |
| metabase-iac plan | Preview what changes would be applied (dry run) |
| metabase-iac apply | Push local resource definitions to Metabase |
| metabase-iac databases | List connected databases |
| metabase-iac collections | List collections |
Options
pull
-o, --output <dir> Output directory (default: ".")
--import-base <path> Import path for types in generated files (default: "metabase-iac/types")
plan / apply
-d, --dir <dir> Resources directory (default: ".")Authentication
Configure via environment variables or a .env file:
METABASE_URL=https://your-metabase.example.com
# Option 1: Username + password
[email protected]
METABASE_PASSWORD=your-password
# Option 2: Session token
METABASE_SESSION_TOKEN=your-session-token
# Option 3: API key
METABASE_API_KEY=mb_xxxxResource types
Collections
import { collection } from 'metabase-iac/types'
export default collection({
id: 23,
slug: 'engineering',
name: 'Engineering',
description: 'Engineering team dashboards',
parent: 'Teams', // optional — omit for root
})Native queries (SQL)
Two files: a .sql with the query and a .meta.json with metadata.
-- questions/daily-signups.sql
SELECT date_trunc('day', created_at) AS day, count(*)
FROM users
GROUP BY 1
ORDER BY 1 DESC
LIMIT 30{
"id": 42,
"name": "Daily Signups",
"collection": "Engineering",
"database": "production",
"display": "line",
"description": "Daily user signups over the last 30 days"
}Native queries (MongoDB)
Same pattern, with .mongodb extension:
[
{ "$match": { "status": "active" } },
{ "$group": { "_id": "$region", "count": { "$sum": 1 } } },
{ "$sort": { "count": -1 } }
]Structured queries (MBQL)
import { structuredQuestion } from 'metabase-iac/types'
export default structuredQuestion({
id: 37,
name: 'accounts-count',
collection: 'Engineering',
database: 'production',
display: 'scalar',
datasetQuery: {
stages: [{ aggregation: [["count", {}]], "source-table": 77 }],
database: 2,
},
})Dashboards
import { dashboard } from 'metabase-iac/types'
export default dashboard({
id: 3,
name: 'Engineering Overview',
collection: 'Engineering',
cards: [
{ question: 'Daily Signups', questionId: 42, width: 12, height: 6, row: 0, col: 0 },
{ question: 'accounts-count', questionId: 37, width: 6, height: 3, row: 0, col: 12 },
{ text: '## Notes\nUpdated daily.', width: 24, height: 2, row: 6, col: 0 },
],
})Workflow
pull → review diff → edit → plan → apply → commitmetabase-iac pull— import current state from Metabase- Review changes with
git diff - Edit
.ts/.sql/.mongodbfiles as needed metabase-iac plan— preview what will changemetabase-iac apply— push to Metabasegit commit— version control your changes
Project structure
A consumer repo looks like this:
my-metabase/
├── collections/
│ └── *.ts
├── questions/
│ ├── *.ts # Structured (MBQL) queries
│ ├── *.sql # Native SQL queries
│ ├── *.mongodb # Native MongoDB queries
│ └── *.meta.json # Metadata for native queries
├── dashboards/
│ └── *.ts
├── .env # Credentials (gitignored)
├── .gitignore
├── package.json
└── tsconfig.json # Optional — for IDE type checkingHow it works
- Pull reads from the Metabase REST API and generates typed resource files
- Plan/Apply loads local resource files, diffs against a state file (
.metabase-state.json), and creates/updates resources via the API - Resources are matched by name within their collection
- Ordering is automatic: collections are created before questions, questions before dashboards
- Both SQL and MongoDB databases are fully supported — native queries are stored as plain text files
License
MIT
