backstage-api.sh
Generic wrapper script for querying the local Backstage API with automatic authentication.
Features
- Auto-detects kubectl context and extracts cluster name
- Extracts API token from cluster-specific config file
- Supports optional jq filtering
- Works from anywhere in the workspace
Usage
./scripts/backstage-api.sh '<endpoint>' [jq-filter]
⚠️ IMPORTANT: Always quote the endpoint when using query parameters (?filter=...)
# ✅ Correct (with quotes)
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template'
# ❌ Wrong (without quotes - shell will expand the ?)
./scripts/backstage-api.sh /api/catalog/entities?filter=kind=Template
Examples
Basic Usage
# Get all entities
./scripts/backstage-api.sh /api/catalog/entities
# Get all templates (note the quotes!)
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template'
# Get specific entity
./scripts/backstage-api.sh /api/catalog/entities/by-name/template/default/cs-api-realm-template
With jq Filters
# Get template names only
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | .metadata.name'
# Get templates with tags
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | {name: .metadata.name, tags: .metadata.tags}'
# Check source tags
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | {name: .metadata.name, source_tags: (.metadata.tags | map(select(startswith("source:"))))}'
# Group by source tags
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | {name: .metadata.name, source_tags: (.metadata.tags | map(select(startswith("source:"))))}' | jq -s 'group_by(.source_tags | sort | join(",")) | map({source_tags: .[0].source_tags, count: length, examples: (map(.name) | .[0:5])})'
Filter Specific Templates
# Get local file template
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | select(.metadata.name == "cs-api-realm-template")'
# Get GitHub templates
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | select(.metadata.tags | contains(["source:github-discovered"]))'
# Get Crossplane templates
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Template' '.[] | select(.metadata.tags | contains(["crossplane"]))'
Other Endpoints
# Get components
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=Component'
# Get APIs
./scripts/backstage-api.sh '/api/catalog/entities?filter=kind=API'
# Search
./scripts/backstage-api.sh '/api/search/query?term=crossplane'
How it Works
- Detects current kubectl context (e.g.,
osp-openportal,osp-openportal-oidc) - Extracts cluster name from context (e.g.,
openportal) - Looks for
app-config.openportal.local.yamlin app-portal/ - Extracts the static API token from the config
- Makes authenticated request to
http://localhost:7007 - Optionally pipes through jq for filtering
Note: Multiple contexts pointing to the same cluster (e.g., with different auth methods) share the same config file.
Requirements
- Backstage running on
http://localhost:7007 - Cluster-specific config file with static API token (generated by
cluster-config.sh) kubectlconfigured with current contextjqinstalled for JSON processing
Claude Code Integration
To allow Claude Code to use this script without manual confirmation, add it to your allowed commands in .claude/settings.local.json:
{
"allowedCommands": [
"Bash(./scripts/backstage-api.sh:*)"
]
}
Benefit: More secure than allowing curl globally - restricts Claude to only this specific script.
Troubleshooting
Config file not found:
# Check current context and cluster name
kubectl config current-context
kubectl config view --minify -o jsonpath='{.contexts[0].context.cluster}'
# Verify config file exists (should match cluster name)
ls app-portal/app-config.*.local.yaml
# Re-run cluster-config.sh to generate config
./scripts/cluster-config.sh
No token found:
# Check token in config
grep -A2 "type: static" app-portal/app-config.*.local.yaml
Connection refused:
# Verify Backstage is running
curl http://localhost:7007/api/catalog/entities | head