From datajunction
Direct REST API authoring for DataJunction nodes using curl/HTTP clients. Useful for exploration, prototyping, and non-repo-backed namespaces.
How this skill is triggered — by the user, by Claude, or both
Slash command
/datajunction:datajunction-apiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Direct REST API authoring for DJ nodes. Use this skill for quick exploration, prototyping, or working in namespaces that don't use the repo-backed workflow.
Direct REST API authoring for DJ nodes. Use this skill for quick exploration, prototyping, or working in namespaces that don't use the repo-backed workflow.
For the modeling work upstream of any authoring (decomposition, naming, ratio decomposition, etc.), see datajunction-semantic-model. For the production-path equivalent of these patterns in YAML, see datajunction-repo.
✅ MUST use repo workflow instead (datajunction-repo) for:
git_only: true) — direct API changes are rejected✅ Should use repo workflow for:
✅ API workflow is appropriate for:
Before authoring via API, verify the target namespace allows it.
Best approach — get_node_details MCP tool (datajunction-query skill):
get_node_details(name="finance.total_revenue")
The response will include git repository information:
Git Repository:
Repo: owner/dj-finance
Branch: main
Default Branch: main
→ This namespace is repo-backed (use git workflow for changes)
Alternative — REST API (shows read-only status):
curl -b ~/.dj/cookies.txt -X GET $DJ_URL/namespaces/finance/git
# Response:
{
"github_repo_path": "owner/dj-finance",
"git_branch": "main",
"default_branch": "main",
"git_path": "nodes/",
"git_only": true ← If true, namespace is read-only (API changes blocked)
}
Decision tree:
git_only: true: MUST use repo workflow (API changes will fail)git_only: false: Can use either workflowSELECT <aggregation_expression> AS <metric_name>
FROM <single_node>
Metrics select a single expression from a single source, transform, or dimension node. They cannot contain WHERE clauses — use CASE WHEN instead. See datajunction-semantic-model for the modeling rationale.
Required:
name — Fully qualified metric name (e.g., finance.total_revenue)query — SQL aggregation expressionRecommended:
description — Human-readable descriptionmetric_metadata.direction — higher_is_better / lower_is_better / neutralmetric_metadata.unit — dollar / unitless (⚠️ NOT count — server rejects)mode — draft / publishedrequired_dimensions — Dimensions required for this metric to make senseowners — List of email addresses (prefer team emails)COUNT:
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
-H 'Content-Type: application/json' \
-d '{
"name": "finance.num_transactions",
"description": "Total number of transactions",
"query": "SELECT COUNT(transaction_id) AS num_transactions FROM finance.transactions",
"owners": ["[email protected]"],
"mode": "published"
}'
SUM:
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
-H 'Content-Type: application/json' \
-d '{
"name": "finance.total_revenue",
"description": "Total revenue from all transactions",
"query": "SELECT SUM(amount_usd) AS total_revenue FROM finance.transactions",
"metric_metadata": {
"direction": "higher_is_better",
"unit": "dollar"
},
"owners": ["[email protected]"],
"mode": "published"
}'
Conditional aggregation (CASE WHEN, not WHERE):
curl -b ~/.dj/cookies.txt -X POST $DJ_URL/nodes/metric/ \
-H 'Content-Type: application/json' \
-d '{
"name": "finance.completed_revenue",
"description": "Revenue from completed non-refund transactions",
"query": "
SELECT SUM(
CASE
WHEN status = '\''completed'\'' AND refund_flag = false
THEN amount_usd
ELSE 0
END
) AS completed_revenue
FROM finance.transactions
",
"metric_metadata": {
"direction": "higher_is_better",
"unit": "dollar"
},
"owners": ["[email protected]"],
"mode": "published"
}'
Ratio over base metrics (decompose first, then derive — see datajunction-semantic-model):
# Step 1: create the base metrics (one curl each)
curl -X POST $DJ_URL/nodes/metric/ -d '{
"name": "finance.clicks",
"query": "SELECT COUNT_IF(event = '\''click'\'') FROM finance.events",
"owners": ["[email protected]"],
"mode": "published"
}'
curl -X POST $DJ_URL/nodes/metric/ -d '{
"name": "finance.impressions",
"query": "SELECT COUNT_IF(event = '\''impression'\'') FROM finance.events",
"owners": ["[email protected]"],
"mode": "published"
}'
# Step 2: derived ratio metric referencing the base metrics
curl -X POST $DJ_URL/nodes/metric/ -d '{
"name": "finance.conversion_rate",
"description": "Click-through rate as percentage",
"query": "SELECT finance.clicks * 100.0 / NULLIF(finance.impressions, 0)",
"metric_metadata": {
"direction": "higher_is_better",
"unit": "unitless"
},
"owners": ["[email protected]"],
"mode": "published"
}'
DJ automatically handles divide-by-zero, but NULLIF() is extra safety.
Same pattern as metrics — POST JSON to the appropriate endpoint:
POST /nodes/source/ — source nodes (catalog/schema/table refs)POST /nodes/dimension/ — dimension nodesPOST /nodes/transform/ — transform nodesPOST /nodes/cube/ — cubes (metric + dimension combinations)For the YAML-equivalent shapes of each, see datajunction-repo — the field set is the same, just expressed in JSON instead of YAML.
Update (PATCH):
curl -b ~/.dj/cookies.txt -X PATCH $DJ_URL/nodes/finance.total_revenue/ \
-H 'Content-Type: application/json' \
-d '{"description": "Updated description"}'
Deactivate (soft delete):
curl -b ~/.dj/cookies.txt -X DELETE $DJ_URL/nodes/finance.total_revenue/
Deactivated nodes can be revived. For hard-delete (irreversible), use the dj CLI: dj delete-node finance.total_revenue --hard.
npx claudepluginhub datajunction/dj --plugin datajunctionActivates when working with DataJunction semantic layer. Covers core concepts, node types, star schema modeling, and dimension links. Use with companion skills for querying, modeling, or authoring nodes.
Guides step-by-step in defining business metrics (aggregations) on Honeydew entities. Covers SQL expression building and deployment via MCP tools.
Build, validate, and manage semantic models using Sidemantic. Creates semantic layers mapping database tables to business dimensions/metrics, generates SQL, and imports from Cube/dbt/LookML.