Skip to content
Back to skills

Grafana Foundation Sdk

ASecurity

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.

  • 37 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 21, 2026
ai-agentstypescriptpythongojavaphpbashsqlexpresskubernetesgit

Works with

  • api

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 5 files and shows the line behind each finding

Scanned September 21, 2026

npx -y skills add tenequm/claude-plugins --skill grafana-foundation-sdk --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Grafana Foundation Sdk?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Grafana Foundation Sdk
[![Security: A β€” Skills Directory](https://www.skillsdirectory.com/api/skills/tenequm-grafana-foundation-sdk-skills/badge)](https://www.skillsdirectory.com/skills/tenequm-grafana-foundation-sdk-skills)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: grafana-foundation-sdk
description: 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.
metadata:
  internal: true
  version: "0.2.4"
  categories: "operations, development"
  topics: "grafana, dashboards, observability, dashboards-as-code, typescript"
  upstream: "@grafana/grafana-foundation-sdk@0.0.16, github.com/grafana/grafana-foundation-sdk/go@0.0.16"
  openclaw:
    homepage: https://github.com/tenequm/skills/tree/main/skills/grafana-foundation-sdk
    emoji: "πŸ“Š"
---

# 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/go@v0.0.16
```

## 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="myapp"}, 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="myapp", app=~"$service", level=~"$level"}')
  .refId('A')

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

### 8. Transformations

Transformations are applied as raw objects since the SDK doesn't have typed builders for all transformation types:

```typescript
new TableBuilder()
  .withTransformation({
    id: 'reduce',
    options: {
      reducers: ['lastNotNull'],
      mode: 'seriesToRows',
      includeTimeField: false,
      labelsToFields: true,
    },
  })
  .withTransformation({
    id: 'organize',
    options: {
      excludeByName: { Field: true },
      renameByName: { buyer_wallet: 'Buyer Wallet', 'Last not null': 'Requests' },
    },
  })
  .withTransformation({
    id: 'sortBy',
    options: { sort: [{ field: 'Requests', desc: true }] },
  })
```

## Generating Output

The `.build()` call returns a plain object matching Grafana's dashboard JSON schema. Serialize it however you need:

```typescript
// Standard JSON file (for provisioning or ConfigMaps)
const fs = require('fs');
const dashboard = builder.build();
fs.writeFileSync('dashboard.json', JSON.stringify(dashboard, null, 2));

// Kubernetes resource manifest (for Grafana's k8s API)
const manifest = {
  apiVersion: 'dashboard.grafana.app/v1beta1',
  kind: 'Dashboard',
  metadata: { name: dashboard.uid },
  spec: dashboard,
};
console.log(JSON.stringify(manifest, null, 2));
```

## Known Gotchas

These are sharp edges discovered from real usage and open issues on the SDK repo:

1. **SDK is v0.0.x (public preview)** - Used by Grafana Labs in production but the API can change between releases. Pin your version explicitly. Best suited for Grafana >= 12, works with >= 10.

2. **`instant()` and `range()` are mutually exclusive in Prometheus** - Calling `.instant()` sets `instant=true` AND `range=false`. Calling `.range()` does the opposite. Use `.rangeAndInstant()` if you need both.

3. **Loki `range()`/`instant()` are deprecated** - Use `.queryType('range')` or `.queryType('instant')` instead. Similarly, `.resolution()` is deprecated in favor of `.step()`.

4. **First threshold step must have `value: null`** - This is the base/default color. Omitting it produces invalid JSON.

5. **Panel IDs are auto-assigned** - You don't set `id` on panels. Grafana assigns them at import time. Similarly, `gridPos.x/y` are computed from `height()` and `span()`.

6. **Transformations are plain objects** - The SDK has no typed builders for transformations. Pass them as raw `{ id, options }` objects via `.withTransformation()`.

7. **CustomVariable quirk** - When provisioning via Grafana's API, `CustomVariableBuilder` requires the `.query()` field with comma-separated key:value pairs (e.g., `'All : .+, Error : error'`) for options to persist, even when `.values()` is also used.

8. **Go: `cog.ToPtr()` is essential** - Many struct fields are pointer types. Use `cog.ToPtr[T](value)` for nullable fields (thresholds, datasource refs). TypeScript doesn't have this issue.

9. **Go: `Build()` returns error** - Always check it. TypeScript's `.build()` returns the object directly with compile-time type safety instead.

10. **No typed query builders for plugin datasources** - Only core datasources (Prometheus, Loki, Tempo, Elasticsearch, CloudWatch, etc.) have builders. For third-party plugins, define custom query types by implementing the `Builder<Dataquery>` interface.

11. **Dashboard schema v1 vs v2** - This skill targets the v1 dashboard (`@grafana/grafana-foundation-sdk/dashboard`, k8s apiVersion `dashboard.grafana.app/v1beta1`). A newer schema v2 ships as `dashboardv2beta1` (k8s apiVersion `dashboard.grafana.app/v2beta1`) with its own builders. v2beta1 is still stabilizing and has known sharp edges (e.g. transforms, annotation positioning, SQL expressions in Go) - prefer v1 unless you specifically need v2 layouts. Most query/panel builders are shared; some expose a `QueryV2Builder`/`VisualizationV2Builder` variant for v2.

12. **Builders are only type-checked if wired into a tsconfig** - The SDK gives compile-time safety only when the generator file is actually type-checked. A generator sitting under a non-package directory (e.g. a Helm chart dir) that no `tsconfig` includes is silently unchecked, so type errors surface only at `.build()` runtime. Also: the SDK's output targets ES2024/`bundler` module resolution, which an older global `tsc` chokes on - run the project-local compiler (`npx tsc`), not a stale global one.

13. **Regenerate JSON after every generator edit** - The deployed dashboard is the generated JSON, not the `.ts`/`.go` source. Edit the generator, re-run it, and commit the regenerated JSON together; never hand-edit the generated JSON (the next regen silently overwrites it). A repo rule ("never edit the dashboard JSON directly") is worth adding.

## Reference Files

For detailed API reference and complete examples, see:
- `references/typescript-api.md` - Full TypeScript API with all panel types, query builders, and configuration options
- `references/patterns.md` - Common dashboard patterns, recipes, and a complete example converting this project's dashboard to SDK code

Files in this skill

  • CHANGELOG.md3.3 KB
  • LICENSE.txt8.9 KB
  • SKILL.md13.4 KB
  • references/patterns.md13.1 KB
  • references/typescript-api.md17.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.