Skip to main content

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.

  1. 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
  2. Go to the directory of your newly created plugin:

    cd <your-plugin>
  3. Install the dependencies:

    npm install
  4. Build the plugin frontend:

    npm run dev
  5. In a new terminal window, build the plugin backend:

    mage -v build:linux
  6. Start Grafana:

    docker compose up
  7. 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/folderdescription
Magefile.goIt’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.jsonA JSON file describing the plugin.
/pkg/main.goStarting 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:

propertydescription
backendSet to true for backend plugins. This tells Grafana that it should start a binary when loading the plugin.
executableThis is the name of the executable that Grafana expects to start. Refer to plugin.json reference for details.
alertingIf 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:

src/app.go
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:

src/resources.go
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.

Your scaffolded app plugin already has a default CallResource that uses ServeMux. It looks like this:

app.go
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.

resources.go
// 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.

app.go
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
note

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.

note

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.