Add a backend component to an app plugin
A backend component for an app plugin allows you to extend the app plugin for additional functionality such as custom authentication methods and integration with other services.
The following are typical use cases for backend components in app plugins:
- Use custom authentication methods that aren't supported in Grafana
- Use authorization checks that aren't supported in Grafana
- Run workloads on the server side
- Connect to non-HTTP services that normally can't be connected to from a browser
Before you beginβ
Install the following prerequisites before adding a backend component:
Create a new app pluginβ
The Grafana create-plugin tool is a CLI application that simplifies Grafana plugin development, so that you can focus on code. The tool scaffolds a starter plugin, all the required configuration, and a development environment using Docker Compose for you.
-
In a new directory, create a plugin from a template using the create-plugin tool. When prompted for the kind of plugin, select app and answer yes to "Do you want a backend part of your plugin?":
npx @grafana/create-plugin@latest
-
Go to the directory of your newly created plugin:
cd <your-plugin>
-
Install the dependencies:
npm install
-
Build the plugin frontend:
npm run dev
-
In a new terminal window, build the plugin backend:
mage -v build:linux
-
Start Grafana:
docker compose up
- Open Grafana, by default http://localhost:3000/, and then go to Administration > Plugins. Make sure that your app plugin is there.
You can also verify that Grafana has discovered the plugin by checking the logs:
INFO[01-01|12:00:00] Plugin registered logger=plugin.loader pluginID=<your-plugin>
Anatomy of a backend pluginβ
The folders and files used to build the backend for the app are:
file/folder | description |
---|---|
Magefile.go | Itβs not a requirement to use mage build files, but we strongly recommend using them so that you can use the build targets provided by the plugin SDK. |
/go.mod | Go modules dependencies. |
/src/plugin.json | A JSON file describing the plugin. |
/pkg/main.go | Starting point of the plugin binary. |
The plugin.json fileβ
The plugin.json
file is required for all plugins. When building a backend plugin, pay attention especially to these properties:
property | description |
---|---|
backend | Set to true for backend plugins. This tells Grafana that it should start a binary when loading the plugin. |
executable | This is the name of the executable that Grafana expects to start. Refer to plugin.json reference for details. |
alerting | If your backend data source supports alerting, set to true . Requires backend to be set to true . |
Add authentication to your app pluginβ
To learn more about adding authentication to your app plugin (for example, to call a custom backend or third-party API) and handling secrets, refer to Add authentication for app plugins.
Access app settingsβ
Settings are part of the AppInstanceSettings
struct. They are passed to the app plugin constructor as the second argument. For example:
func NewApp(ctx context.Context, settings backend.AppInstanceSettings) (instancemgmt.Instance, error) {
jsonData := settings.JSONData // json.RawMessage
secureJsonData := settings.DecryptedSecureJSONData // map[string]string
}
You can also get the settings from a request Context
:
func (a *App) handleMyRequest(w http.ResponseWriter, req *http.Request) {
pluginConfig := backend.PluginConfigFromContext(req.Context())
jsonData := pluginConfig.AppInstanceSettings.JSONData // json.RawMessage
}
Add a custom endpoint to your app pluginβ
Here's how to add a ServeMux
or CallResource
endpoint to your app plugin.
ServeMux (recommended)β
Your scaffolded app plugin already has a default CallResource
that uses ServeMux
. It looks like this:
type App struct {
backend.CallResourceHandler
}
// NewApp creates a new example *App instance.
func NewApp(_ context.Context, _ backend.AppInstanceSettings) (instancemgmt.Instance, error) {
var app App
// Use an httpadapter (provided by the SDK) for resource calls. This allows us
// to use a *http.ServeMux for resource calls, so we can map multiple routes
// to CallResource without having to implement extra logic.
mux := http.NewServeMux()
app.registerRoutes(mux)
// implement the CallResourceHandler interface
app.CallResourceHandler = httpadapter.New(mux)
return &app, nil
}
Now you can add custom endpoints to your app plugin.
The scaffolded code already contains a resources.go
file with the registerRoutes
function.
// this function already exists in the scaffolded app plugin
func (a *App) registerRoutes(mux *http.ServeMux) {
mux.HandleFunc("/myCustomEndpoint", a.handleMyCustomEndpoint)
}
func (a *App) handleMyCustomEndpoint(w http.ResponseWriter, r *http.Request) {
// handle the request
// e.g. call a third-party API
w.Write([]byte("my custom response"))
w.WriteHeader(http.StatusOK)
}
CallResourceβ
You can also add custom endpoints to your app plugin by adding a CallResource
handler to your backend component directly. You must implement the logic to handle multiple requests.
func (a *App) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
switch req.Path {
case "myCustomEndpoint":
sender.Send(&backend.CallResourceResponse{
Status: http.StatusOK,
Body: []byte("my custom response"),
})
default:
return sender.Send(&backend.CallResourceResponse{
Status: http.StatusNotFound,
})
}
return nil
}
You can also see the data sources documentation on resource handler which you can also apply to your app plugin.
Call your custom endpoint from frontend codeβ
To call your custom endpoint from frontend code, you can use the fetch
function from getBackendSrv
. For example:
import { getBackendSrv } from '@grafana/runtime';
import { lastValueFrom } from 'rxjs';
function getMyCustomEndpoint() {
const response = await getBackendSrv().fetch({
// replace ${PLUGIN_ID} with your plugin id
url: '/api/plugins/${PLUGIN_ID}/myCustomEndpoint',
});
return await lastValueFrom(response);
}
Troubleshootingβ
Grafana doesn't load my pluginβ
Ensure that Grafana has been started in development mode. If you are running Grafana from source, add the following line to your conf/custom.ini
file:
app_mode = development
If you don't have a conf/custom.ini
file already, create it before proceeding.
You can then start Grafana in development mode by running make run & make run-frontend
in the Grafana repository root.
If you are running Grafana from a binary or inside a Docker container, you can start it in development mode by setting the environment variable GF_DEFAULT_APP_MODE
to development
.
By default, Grafana requires backend plugins to be signed. To load unsigned backend plugins, you need to configure Grafana to allow unsigned plugins. For more information, refer to Plugin signature verification.
Exampleβ
Refer to our example app plugin with backend for a complete example.