Open-Source Wikis

/

Grafana

/

How to contribute

/

Patterns and conventions

grafana/grafana

Patterns and conventions

Conventions that recur across the codebase. New code is expected to match.

Backend (Go)

Service-per-domain

Each business domain has a folder under pkg/services/<domain>/. The standard layout is:

pkg/services/<domain>/
├── <domain>.go                # Service interface + types
├── <domain>impl/              # Concrete implementation
├── <domain>test/              # Test helpers / fakes
├── api/                       # HTTP handlers (sometimes inlined into pkg/api/)
└── store/                     # SQL queries (sometimes named `database/`)

Services define their public surface as a Go interface; consumers depend on the interface, not the struct. Concrete implementations are wired in pkg/server/wire.go.

Wire DI

Adding a service is a four-step dance:

  1. Define the interface and constructor (ProvideService(...)).
  2. Add the constructor to the right wire.NewSet in pkg/server/wire.go (or in a service-local wire set imported from there).
  3. Inject the dependency where you need it.
  4. Run make gen-go to regenerate wire_gen.go. Wire catches circular deps at codegen time, not at runtime.

Error handling

  • Return errutil.Error types from public APIs (pkg/util/errutil/) so they map cleanly to HTTP status codes.
  • Avoid bare fmt.Errorf for user-visible errors — wrap in a typed error so the API layer can attach a status.
  • Don't swallow errors. If you can't handle, return.

Logging

log.New("<namespace>") is the canonical pattern. Namespaces are dotted (alerting.scheduler, secrets.gcp). Be specific so users can tune levels per area in [log.<namespace>].

Database access

  • Legacy: pkg/services/sqlstore/ (xorm). New domain code increasingly hides this behind a domain-specific store interface.
  • Modern: app-platform resources go through pkg/storage/unified/ and the apiserver layer.
  • All migrations are forward-only and live in pkg/services/sqlstore/migrations/. Adding one requires registering it in the migrator init and being aware of cross-database (SQLite/MySQL/Postgres) compatibility.

gRPC and plugins

Plugin communication and most internal cross-service streaming uses gRPC + protobuf. Protos live next to their implementations (e.g. pkg/storage/unified/resourcepb/). Regenerate with the appropriate make target.

Comments

From AGENTS.md: only add a comment when it explains why or surfaces non-obvious logic. No tracking links (Slack/Jira/GitHub) inside code.

Frontend (TypeScript / React)

Redux Toolkit and RTK Query

  • Use Redux Toolkit slices, not the legacy connect-style reducers.
  • Server state lives in RTK Query APIs (per-feature <feature>Api.ts files). Local UI state lives in slices.
  • Action types and selectors are typed via slice exports; avoid string literals.

Components

  • Function components with hooks. No class components in new code.
  • Style with Emotion via useStyles2(getStyles). The getStyles function takes the theme.
  • Use components from @grafana/ui (Button, Field, Select, Stack, …) instead of building your own primitives.

File and naming conventions

  • Component file: MyThing.tsx. Test: MyThing.test.tsx. Hook: useMyThing.ts. Constant/enum: myThing.types.ts or constants.ts adjacent to use.
  • Feature folders under public/app/features/<feature>/ typically contain state/ (slice / api), components/, pages/, hooks/, and types.ts.
  • Avoid default exports for components — named exports keep refactors easier.

i18n

  • Strings rendered in the UI go through <Trans> (children) or t() (programmatic) from @grafana/i18n.
  • After adding new strings, run make i18n-extract to update the extracted catalog.
  • The repo does not accept community-submitted translations of grafana.json files; only the marking of strings.

Theming

  • All colors and tokens come from the active theme (theme.colors.*, theme.shape.*, theme.typography.*). Don't hard-code colors.
  • A custom ESLint rule (@grafana/theme-token-usage) flags raw color literals in some areas.

Plugin extension points

  • New extension points for plugins go through @grafana/runtime's usePluginExtensions / usePluginLinks. Add the corresponding addedComponents / addedLinks declaration in your plugin's plugin.json.

Cross-cutting

Feature toggles

  • Add toggles in pkg/services/featuremgmt/registry.go, then run make gen-feature-toggles.
  • In Go: features.IsEnabled(ctx, "myToggle"). In TS: config.featureToggles.myToggle.
  • Mark the stage (Experimental, PrivatePreview, PublicPreview, GA, Deprecated) — new toggles default to Experimental.

Schema changes

  • CUE definitions in kinds/ generate Go and TS. Edit the CUE, run make gen-cue, commit the regenerated outputs alongside the source change.
  • App-platform schemas under apps/<name>/kinds/ generate via make gen-apps.

Security defaults

  • Treat all user input as untrusted. The codebase has dedicated escapers for SQL (sqlstore), shell (none — avoid shelling out), and HTML (@grafana/data textUtil).
  • Don't log secrets. Helpers in pkg/services/secrets/ wrap encryption.
  • Frontend XSS: never dangerouslySetInnerHTML user content; use <SanitizedHtml> or the safe textUtil helpers.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Patterns and conventions – Grafana wiki | Factory