Claude
Skills
Sign in
Back

grafana-foundation-sdk

Included with Lifetime
$97 forever

Build Grafana dashboards as code with the grafana-foundation-sdk typed builders (TypeScript or Go). Use when creating, modifying, or generating Grafana dashboard JSON programmatically, converting hand-written dashboard JSON to typed code, building monitoring dashboards, or working with Prometheus/Loki queries in dashboards.

Backend & APIs

What this skill does


# Grafana Foundation SDK

The grafana-foundation-sdk provides strongly typed builder libraries for defining Grafana dashboards as code. Instead of writing raw JSON (which is error-prone and hard to review in diffs), you compose dashboards using chained builder calls that produce valid Grafana JSON.

The SDK is auto-generated from Grafana's internal CUE schemas via the `cog` tool. It supports Go, TypeScript, Python, PHP, and Java. This skill focuses on **TypeScript** (primary) and **Go** (secondary) since those are the most common choices for infrastructure teams.

## When to use this skill

- Creating new Grafana dashboards from scratch
- Converting existing hand-written dashboard JSON to typed code
- Adding panels, variables, or queries to dashboards
- Building reusable dashboard components (helper functions for common panel patterns)
- Generating dashboards dynamically based on service lists or configs

## Installation

The SDK is published as concrete `v0.0.x` tags (latest: **v0.0.16**). Pin explicitly - it is pre-1.0 and the API churns between releases (see Known Gotchas).

**TypeScript:**
```bash
npm install '@grafana/grafana-foundation-sdk@~0.0.16'
# or
pnpm add '@grafana/grafana-foundation-sdk@~0.0.16'
```

**Go:**
```bash
go get github.com/grafana/grafana-foundation-sdk/[email protected]
```

## Core Architecture

Everything follows the **builder pattern**: create a builder, chain configuration methods, call `.build()` (TS) or `.Build()` (Go) to produce the final object. The output is standard Grafana dashboard JSON - compatible with Grafana's API, file-based provisioning, and Kubernetes ConfigMaps.

Each panel type, query type, and variable type lives in its own package. You import only what you need:

```typescript
// Each concern has its own import
import { DashboardBuilder, RowBuilder } from '@grafana/grafana-foundation-sdk/dashboard';
import { PanelBuilder as TimeseriesBuilder } from '@grafana/grafana-foundation-sdk/timeseries';
import { PanelBuilder as StatBuilder } from '@grafana/grafana-foundation-sdk/stat';
import { DataqueryBuilder as PromQueryBuilder } from '@grafana/grafana-foundation-sdk/prometheus';
import { DataqueryBuilder as LokiQueryBuilder } from '@grafana/grafana-foundation-sdk/loki';
```

## Quick Start - TypeScript

```typescript
import { DashboardBuilder, RowBuilder, QueryVariableBuilder } from '@grafana/grafana-foundation-sdk/dashboard';
import { PanelBuilder as StatBuilder } from '@grafana/grafana-foundation-sdk/stat';
import { PanelBuilder as TimeseriesBuilder } from '@grafana/grafana-foundation-sdk/timeseries';
import { DataqueryBuilder } from '@grafana/grafana-foundation-sdk/prometheus';
import * as common from '@grafana/grafana-foundation-sdk/common';

const dashboard = new DashboardBuilder('My Service Overview')
  .uid('my-service-overview')
  .tags(['my-service'])
  .editable()
  .refresh('30s')
  .time({ from: 'now-24h', to: 'now' })
  .timezone('browser')
  .withVariable(
    new QueryVariableBuilder('service')
      .label('Service')
      .query('label_values(up{namespace="default"}, job)')
      .datasource({ type: 'prometheus', uid: 'prometheus' })
      .refresh(1)
      .includeAll(true)
      .allValue('.*')
      .sort(1)
  )
  .withRow(new RowBuilder('Overview'))
  .withPanel(
    new StatBuilder()
      .title('Request Rate')
      .datasource({ type: 'prometheus', uid: 'prometheus' })
      .withTarget(
        new DataqueryBuilder()
          .expr('sum(rate(http_requests_total{job=~"$service"}[5m]))')
          .legendFormat('req/s')
      )
      .unit('reqps')
      .decimals(1)
      .height(4)
      .span(6)
      .colorMode(common.BigValueColorMode.Background)
      .graphMode(common.BigValueGraphMode.Area)
      .reduceOptions(
        new common.ReduceDataOptionsBuilder().calcs(['lastNotNull'])
      )
  )
  .withPanel(
    new TimeseriesBuilder()
      .title('Request Rate Over Time')
      .datasource({ type: 'prometheus', uid: 'prometheus' })
      .withTarget(
        new DataqueryBuilder()
          .expr('sum by (job)(rate(http_requests_total{job=~"$service"}[5m]))')
          .legendFormat('{{job}}')
      )
      .unit('reqps')
      .fillOpacity(15)
      .height(8)
      .span(12)
  );

// Output the dashboard JSON
console.log(JSON.stringify(dashboard.build(), null, 2));
```

## Key Patterns

### 1. Helper functions for repeated panel configurations

The biggest win from using the SDK is creating reusable helpers that encode your team's conventions:

```typescript
function promDs() {
  return { type: 'prometheus', uid: 'prometheus' } as const;
}

function lokiDs() {
  return { type: 'loki', uid: 'loki' } as const;
}

function promQuery(expr: string, legend?: string) {
  const q = new DataqueryBuilder().expr(expr);
  if (legend) q.legendFormat(legend);
  return q;
}

function statPanel(title: string, expr: string, opts?: { unit?: string; decimals?: number; color?: string }) {
  const panel = new StatBuilder()
    .title(title)
    .datasource(promDs())
    .withTarget(promQuery(expr))
    .height(4)
    .span(4)
    .colorMode(common.BigValueColorMode.Background)
    .graphMode(common.BigValueGraphMode.Area)
    .reduceOptions(new common.ReduceDataOptionsBuilder().calcs(['lastNotNull']));

  if (opts?.unit) panel.unit(opts.unit);
  if (opts?.decimals !== undefined) panel.decimals(opts.decimals);
  // Thresholds can be set via .thresholds() if needed

  return panel;
}
```

### 2. Template variables

```typescript
// Query variable - populated from Prometheus labels
new QueryVariableBuilder('service')
  .label('Service')
  .query('label_values(http_server_duration_count{namespace="x402"}, job)')
  .datasource({ type: 'prometheus', uid: 'prometheus' })
  .refresh(2)  // 1=on dashboard load, 2=on time range change
  .includeAll(true)
  .allValue('.*')
  .sort(1)  // 1=alphabetical asc

// Custom variable - static key:value pairs
new CustomVariableBuilder('level')
  .label('Log Level')
  .query('All : .+, Error : error|fatal, Warning : warn, Info : info, Debug : debug')
  .current({ text: 'All', value: '.+' })
```

Reference variables in queries with standard Grafana syntax: `$service`, `$__range`, `$__rate_interval`, `$__auto`.

### 3. Panel sizing

Panels use `height(h)` (grid rows) and `span(w)` (out of 24 columns):
- Full width: `.span(24)`
- Half width: `.span(12)`
- Third width: `.span(8)`
- Quarter width: `.span(6)`
- Typical stat panel: `.height(4).span(4)`
- Typical timeseries: `.height(8).span(12)`

### 4. Thresholds

```typescript
import { ThresholdsConfigBuilder } from '@grafana/grafana-foundation-sdk/dashboard';

// First step must have no value (it's the base)
new StatBuilder()
  .thresholds(
    new ThresholdsConfigBuilder()
      .mode(common.ThresholdsMode.Absolute)
      .steps([
        { value: null as any, color: 'green' },
        { value: 80, color: 'yellow' },
        { value: 95, color: 'red' },
      ])
  )
```

### 5. Field overrides

```typescript
new TimeseriesBuilder()
  .overrideByName('Revenue', [
    { id: 'color', value: { fixedColor: 'green', mode: 'fixed' } },
  ])
  .overrideByRegexp('.*5..', [
    { id: 'color', value: { fixedColor: 'red', mode: 'fixed' } },
  ])
```

### 6. Rows (including collapsed)

```typescript
// Regular row
.withRow(new RowBuilder('Traffic'))

// Collapsed row with nested panels
.withRow(
  new RowBuilder('Business Details')
    .collapsed()
    .withPanel(/* ... */)
    .withPanel(/* ... */)
)
```

### 7. Loki log and metric queries

```typescript
import { DataqueryBuilder as LokiQueryBuilder } from '@grafana/grafana-foundation-sdk/loki';

// Log query
new LokiQueryBuilder()
  .expr('{namespace="x402", app=~"$service", level=~"$level"}')
  .refId('A')

// Metric query from logs
new LokiQueryBuilder()
  .expr('sum by (buyer_wallet)(count_over_time({namespace="x402"} | event="request" [$__range]))')
  .legendFormat('{{buyer_wallet}}')
  .refId('A')
```

### 8. Transformations

Transformations are applied as raw objects since the 

Related in Backend & APIs