Documentation Index
Fetch the complete documentation index at: https://mintlify.com/grafana/grafana/llms.txt
Use this file to discover all available pages before exploring further.
Backend architecture
Grafana’s backend is written in Go and follows a clean, service-oriented architecture with compile-time dependency injection.
Directory structure
The backend code lives in pkg/ and is organized by concern:
| Directory | Purpose |
|---|
pkg/api/ | HTTP API handlers and routes |
pkg/services/ | Business logic by domain (alerting, dashboards, auth, etc.) |
pkg/server/ | Server initialization and Wire DI setup |
pkg/tsdb/ | Time series database query backends |
pkg/plugins/ | Plugin system and loader |
pkg/infra/ | Logging, metrics, database access |
pkg/middleware/ | HTTP middleware |
pkg/setting/ | Configuration management |
pkg/models/ | Shared data models |
Dependency injection with Wire
Grafana uses Google Wire for compile-time dependency injection, providing type safety and circular dependency detection.
Wire configuration
The main Wire configuration is in pkg/server/wire.go:
//go:build wireinject
// +build wireinject
package server
import (
"github.com/google/wire"
// ... imports
)
var wireBasicSet = wire.NewSet(
annotationsimpl.ProvideService,
wire.Bind(new(annotations.Repository), new(*annotationsimpl.RepositoryImpl)),
query.ProvideService,
wire.Bind(new(query.Service), new(*query.ServiceImpl)),
// ... more providers
)
func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options) (*Server, error) {
wire.Build(wireExtsSet)
return &Server{}, nil
}
Key Wire patterns used in pkg/server/wire.go:
- Provider functions - Functions like
ProvideService() that return initialized services
- Wire sets - Groups of related providers (
wireBasicSet, withOTelSet)
- Interface bindings -
wire.Bind() to bind implementations to interfaces
- Injector functions -
Initialize() functions that Wire generates implementations for
Regenerating Wire code
After modifying service initialization or dependencies:
This generates pkg/server/wire_gen.go with the actual initialization code.
Common Wire patterns:
// Provider function
func ProvideMyService(db *sqlstore.SQLStore, cfg *setting.Cfg) *MyService {
return &MyService{
db: db,
cfg: cfg,
}
}
// Add to wire.go
var wireSet = wire.NewSet(
ProvideMyService,
wire.Bind(new(MyServiceInterface), new(*MyService)),
)
Service layer architecture
Services contain business logic and are organized by domain.
Service structure
Typical service structure in pkg/services/<domain>/:
pkg/services/dashboards/
├── service/ # Service implementation
│ ├── service.go # Main service
│ └── client/ # Client wrappers
├── database/ # Data access layer
│ └── database.go
├── dashboards.go # Interface definitions
└── models.go # Domain models
Service interface pattern
Services implement interfaces defined in the same package:
// pkg/services/dashboards/dashboards.go
type Service interface {
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error)
SaveDashboard(ctx context.Context, dto *SaveDashboardDTO) (*Dashboard, error)
DeleteDashboard(ctx context.Context, dashboardID int64, orgID int64) error
}
// pkg/services/dashboards/service/service.go
type DashboardServiceImpl struct {
store DashboardStore
cfg *setting.Cfg
}
func (s *DashboardServiceImpl) GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error) {
// Business logic here
}
Key principles:
- Interfaces in the package root, implementations in
service/ or implementation-specific subdirectories
- Business logic in services, not in API handlers
- Services depend on other services through interfaces
- Data access through repository/store patterns
API handlers
HTTP API handlers live in pkg/api/ and delegate to services.
API structure
// pkg/api/dashboard.go
func (hs *HTTPServer) GetDashboard(c *contextmodel.ReqContext) response.Response {
dashboardID := web.Params(c.Req)[":dashboardId"]
// Validation
id, err := strconv.ParseInt(dashboardID, 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "Invalid dashboard ID", err)
}
// Delegate to service
dashboard, err := hs.dashboardService.GetDashboard(c.Req.Context(), &dashboards.GetDashboardQuery{
ID: id,
OrgID: c.OrgID,
})
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to get dashboard", err)
}
return response.JSON(http.StatusOK, dashboard)
}
API handler responsibilities:
- Request parsing and validation
- Authentication/authorization checks
- Calling service methods
- Response formatting
- Error handling
Routing
Routes are registered in pkg/api/api.go:
func (hs *HTTPServer) registerRoutes() {
hs.RouteRegister.Group("/api", func(apiRoute routing.RouteRegister) {
apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) {
dashboardRoute.Get("/db/:slug", hs.GetDashboard)
dashboardRoute.Post("/db", hs.PostDashboard)
dashboardRoute.Delete("/db/:slug", hs.DeleteDashboardBySlug)
})
})
}
Database access
Database access is handled through the sqlstore package.
SQLStore
The main database interface is pkg/services/sqlstore/sqlstore.go:
type SQLStore struct {
cfg *setting.Cfg
engine *xorm.Engine
dialect migrator.Dialect
log log.Logger
}
func (ss *SQLStore) WithDbSession(ctx context.Context, callback dbutil.SessionFunc) error {
return ss.engine.NewSession().WithContext(ctx).Begin(callback)
}
Database patterns:
- XORM for ORM
- Raw SQL for complex queries
- Transactions via
WithDbSession or WithTransactionalDbSession
- Migration system in
pkg/services/sqlstore/migrations/
Example repository
type DashboardStore interface {
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error)
}
type dashboardStore struct {
sqlStore *sqlstore.SQLStore
}
func (s *dashboardStore) GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error) {
dashboard := &Dashboard{}
err := s.sqlStore.WithDbSession(ctx, func(sess *session.SessionDB) error {
exists, err := sess.Where("id = ? AND org_id = ?", query.ID, query.OrgID).Get(dashboard)
if !exists {
return dashboards.ErrDashboardNotFound
}
return err
})
return dashboard, err
}
Plugin backend system
Backend plugins run as separate processes and communicate via gRPC.
Plugin structure
From pkg/plugins/plugins.go:
type Plugin struct {
JSONData // Metadata from plugin.json
FS // File system access
Class // Core or external
Signature // Signature validation
client backendplugin.Plugin
Renderer pluginextensionv2.RendererPlugin
}
// Backend plugin interface
type PluginClient interface {
backend.QueryDataHandler
backend.CheckHealthHandler
backend.CallResourceHandler
backend.StreamHandler
}
Plugin loading
Plugins are discovered and loaded through a pipeline:
- Discovery - Find plugin directories
- Validation - Check signatures and compatibility
- Initialization - Load plugin.json, start backend process
- Registration - Register with plugin registry
See pkg/plugins/manager/pipeline/ for pipeline implementation.
Data source query system
Data source backends live in pkg/tsdb/:
pkg/tsdb/
├── prometheus/ # Prometheus data source
├── loki/ # Loki data source
├── grafana-pyroscope-datasource/
├── cloudwatch/ # AWS CloudWatch
└── ...
Each data source implements the backend.QueryDataHandler interface.
Infrastructure services
Logging
import "github.com/grafana/grafana/pkg/infra/log"
logger := log.New("my-service")
logger.Info("Processing request", "userId", userID)
logger.Error("Failed to save", "error", err)
Metrics
Prometheus metrics via pkg/infra/metrics/:
var requestCounter = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "grafana_api_requests_total",
Help: "Total API requests",
},
[]string{"endpoint", "status"},
)
Tracing
OpenTelemetry tracing via pkg/infra/tracing/:
func (s *Service) DoWork(ctx context.Context) error {
ctx, span := s.tracer.Start(ctx, "Service.DoWork")
defer span.End()
// work here
}
Server initialization
The server starts in pkg/server/server.go:
- Load configuration from
conf/defaults.ini and conf/custom.ini
- Initialize database connection and run migrations
- Wire builds the dependency graph
- Start HTTP server
- Start background services
func (s *Server) Run(ctx context.Context) error {
// Initialize services via Wire
// Start HTTP server
// Start background workers
// Wait for shutdown signal
}
Testing patterns
Unit tests
func TestMyService(t *testing.T) {
mockStore := &MockDashboardStore{}
service := &DashboardServiceImpl{
store: mockStore,
}
result, err := service.GetDashboard(context.Background(), query)
require.NoError(t, err)
assert.Equal(t, expected, result)
}
Integration tests
Integration tests use real databases (SQLite, PostgreSQL, MySQL) via Docker.
Key patterns summary
- Wire DI - Compile-time dependency injection for type safety
- Service interfaces - Business logic behind interfaces
- Thin handlers - API handlers delegate to services
- Repository pattern - Data access abstraction
- Plugin architecture - Extensibility via gRPC
- Migration system - Database schema versioning