# Build a data source plugin

## Introduction[​](#introduction "Direct link to Introduction")

Grafana supports a wide range of [data sources](https://grafana.com/grafana/plugins/data-source-plugins/), including Prometheus, MySQL, and Datadog. In some cases, though, you already have an in-house metrics solution that you’d like to add to your Grafana dashboards. This tutorial teaches you to build a new data source plugin to query your data.

In this tutorial, you'll:

1. [Create a data source plugin](#1-create-a-new-data-source-plugin)
2. [Define your response structure](#2-define-your-response-data-frame)
3. [Implement your query editor](#3-implement-your-query-editor)
4. [Configure your data source using the config editor](#4-enable-configuration-for-your-data-source)

### Prerequisites[​](#prerequisites "Direct link to Prerequisites")

* Grafana v10.0 or later
* [LTS](https://nodejs.dev/en/about/releases/) version of Node.js

Basic understanding of the following topics:

* [Plugin anatomy](#anatomy-of-a-plugin)
* [Data source plugins](#data-source-plugins)
* [Data frames](https://grafana.com/developers/plugin-tools/key-concepts/data-frames.md)

## 1. Create a new data source plugin[​](#1-create-a-new-data-source-plugin "Direct link to 1. Create a new data source plugin")

The Grafana [create-plugin tool](https://www.npmjs.com/package/@grafana/create-plugin) 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](https://docs.docker.com/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 <!-- -->datasource<!-- -->:

   ```shell
   npx @grafana/create-plugin@latest

   ```

2. Go to the directory of your newly created plugin:

   ```shell
   cd <your-plugin>

   ```

3. Install the dependencies:

   ```shell
   npm install

   ```

4. Build the plugin:

   ```shell
   npm run dev

   ```

5. Start Grafana:

   ```shell
   docker compose up

   ```

6. Open Grafana, by default <http://localhost:3000>, and then go to **Administration** > **Plugins**. Make sure that your <!-- -->datasource<!-- --> plugin is there.

You can also verify that Grafana has discovered your plugin by checking the logs:

```text
INFO[01-01|12:00:00] Plugin registered       logger=plugin.loader pluginID=<your-plugin>

```

To learn how to create a backend data source plugin, see [Build a data source plugin backend component](https://grafana.com/developers/plugin-tools/tutorials/build-a-data-source-backend-plugin.md)

## 2. Define your response data frame[​](#2-define-your-response-data-frame "Direct link to 2. Define your response data frame")

There are countless different databases, each with their own ways of querying data. To be able to support all the different data formats, Grafana consolidates the data into a unified data structure called [data frames](https://grafana.com/developers/plugin-tools/key-concepts/data-frames.md).

Let's see how to create and return a data frame from the `query` method. In this step, you'll change the code in the starter plugin to return a [sine wave](https://en.wikipedia.org/wiki/Sine_wave).

### Edit the query code[​](#edit-the-query-code "Direct link to Edit the query code")

1. In the current `query` method, remove the code inside the `map` function.

   The `query` method now look like this:

   src/datasource.ts

   ```ts
   async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
     const { range } = options;
     const from = range!.from.valueOf();
     const to = range!.to.valueOf();

     const data = options.targets.map(target => {
       // Your code goes here.
     });

     return { data };
   }

   ```

2. Create a couple of helper variables for testing purposes.

   The math here only generates mock test data. Replace it later with real values fetched from the data source you want to connect to Grafana.

   src/datasource.ts

   ```ts
   // duration of the time range, in milliseconds.
   const duration = to - from;

   // step determines how close in time (ms) the points will be to each other.
   const step = duration / 1000;

   ```

3. Create two arrays to hold the data and populate them with timestamps and values.

   src/datasource.ts

   ```ts
   const timestamps: number[] = [];
   const values: number[] = [];

   for (let t = 0; t < duration; t += step) {
     timestamps.push(from + t);
     values.push(Math.sin((2 * Math.PI * t) / duration));
   }

   ```

4. Create a data frame with a time field and a number field, passing the arrays to the `values` property.

   src/datasource.ts

   ```ts
   const frame = createDataFrame({
     refId: target.refId,
     fields: [
       { name: 'time', type: FieldType.time, values: timestamps },
       { name: 'value', type: FieldType.number, values: values },
     ],
   });

   ```

   `refId` needs to be set to tell Grafana which query that generated this data frame.

5. Finally, return the data frame. Your query function should look something like this:

   src/datasource.ts

   ```ts
   async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const { range } = options;
    const from = range!.from.valueOf();
    const to = range!.to.valueOf();

    const data = options.targets.map(target => {
      const duration = to - from;
      const step = duration / 1000;

      const timestamps: number[] = [];
      const values: number[] = [];

      for (let t = 0; t < duration; t += step) {
        timestamps.push(from + t);
        values.push(Math.sin((2 * Math.PI * t) / duration));
      }

      return createDataFrame({
        refId: target.refId,
        fields: [
          {
            name: 'time',
            type: FieldType.time,
            values: timestamps
          },
          {
            name: 'value',
            type: FieldType.number,
            values: values
          },
        ],
      });
    });

    return { data };
   }

   ```

### Do a health check[​](#do-a-health-check "Direct link to Do a health check")

Finally, implement the health check function:

1. Signal a successful connection:

   Grafana needs to verify your data source settings before you can save them. Add a simple health check that, in this case, always succeeds:

   src/datasource.ts

   ```ts
   async testDatasource() {
     return {
       status: 'success',
       message: 'Success',
     };
   }

   ```

   When connecting to external data sources, test the connection with the provided settings before returning success.

2. Try it out by creating a new data source instance and building a dashboard.

Your data source is now sending data frames that Grafana can visualize. Try it out by creating a new data source instance and building a dashboard!

info

In this example, you're generating timestamps from the current time range. This means that you'll get the same graph no matter what time range you're using. In practice, you'd instead use the timestamps returned by your database.

Next, see how you can control the frequency of the sine wave by defining a *query*.

## 3. Implement your query editor[​](#3-implement-your-query-editor "Direct link to 3. Implement your query editor")

Most data sources offer a way to query specific data. MySQL and PostgreSQL use SQL, while Prometheus has its own query language, PromQL. No matter what query language your databases are using, Grafana lets you build support for it.

Add support for custom queries to your data source by implementing your own *query editor*, a React component that enables you to build your own queries with a user-friendly graphical interface.

The query editor can be:

* As simple as a text field where you can edit the raw query text.
* A user-friendly form with drop-down menus and switches, that later gets converted into the raw query text sent off to the database.

### Define the query model[​](#define-the-query-model "Direct link to Define the query model")

The first step in designing your query editor is to define its *query model*. The query model defines the user input to your data source.

For example, to be able to control the frequency of the sine wave you need to add another property:

1. Add a new number property called `frequency` to the query model:

   src/types.ts

   ```ts
   export interface MyQuery extends DataQuery {
     queryText?: string;
     constant: number;
     frequency: number;
   }

   ```

2. Set a default value to the new `frequency` property:

   src/types.ts

   ```ts
   export const defaultQuery: Partial<MyQuery> = {
     constant: 6.5,
     frequency: 1.0,
   };

   ```

### Bind the model to a form[​](#bind-the-model-to-a-form "Direct link to Bind the model to a form")

Now that you've defined the query model you wish to support, the next step is to bind the model to a form. The `FormField` is a text field component from `grafana/ui` that lets you register a listener which will be invoked whenever the form field value changes.

1. Define the `frequency` from the `query` object and add a new form field to the query editor to control the new frequency property in the `render` method.

   src/components/QueryEditor.tsx

   ```tsx
   const { queryText, constant, frequency } = query;

   <InlineField label="Frequency" labelWidth={16}>
     <Input onChange={onFrequencyChange} value={frequency || ''} />
   </InlineField>;

   ```

2. Add a event listener for the new property.

   src/components/QueryEditor.tsx

   ```tsx
   const onFrequencyChange = (event: ChangeEvent<HTMLInputElement>) => {
     onChange({ ...query, frequency: parseFloat(event.target.value) });
     // executes the query
     onRunQuery();
   };

   ```

   The registered listener, `onFrequencyChange`, calls `onChange` to update the current query with the value from the form field.

   `onRunQuery();` tells Grafana to run the query after each change. For fast queries, this is recommended to provide a more responsive experience.

### Use the property[​](#use-the-property "Direct link to Use the property")

The new query model is now ready to use in our `query` method.

1. In the `query` method, use the `frequency` property to adjust our equation.

   src/datasource.ts

   ```ts
   frame.add({ time: from + t, value: Math.sin((2 * Math.PI * query.frequency * t) / duration) });

   ```

2. Try it out by changing the frequency in the query for your panel.

## 4. Enable configuration for your data source[​](#4-enable-configuration-for-your-data-source "Direct link to 4. Enable configuration for your data source")

To access a specific data source, you often need to configure things like hostname, credentials, or authentication method. A *config editor* lets you configure your data source plugin. Similar to the query editor, the config editor defines a model and binds it to a form.

In this example, since you're not actually connecting to an external database in our sine wave example, you don't really need many options. However, to show you how you can add an option, add the *wave resolution* as an option. Resolution controls how close in time the data points are to each other. A higher resolution means more points closer together, at the cost of more data being processed.

### Define the options model[​](#define-the-options-model "Direct link to Define the options model")

1. Add a new number property called `resolution` to the options model.

   src/types.ts

   ```ts
   export interface MyDataSourceOptions extends DataSourceJsonData {
     path?: string;
     resolution?: number;
   }

   ```

### Bind the model to a form[​](#bind-the-model-to-a-form-1 "Direct link to Bind the model to a form")

Just like query editor, the form field in the config editor calls the registered listener whenever the value changes.

1. Add a new form field to the query editor to control the new resolution option.

   src/components/ConfigEditor.tsx

   ```tsx
   <InlineField label="Resolution" labelWidth={12}>
     <Input onChange={onResolutionChange} value={jsonData.resolution || ''} placeholder="Enter a number" width={40} />
   </InlineField>

   ```

2. Add a event listener for the new option.

   src/components/ConfigEditor.tsx

   ```ts
   const onResolutionChange = (event: ChangeEvent<HTMLInputElement>) => {
     const jsonData = {
       ...options.jsonData,
       resolution: parseFloat(event.target.value),
     };
     onOptionsChange({ ...options, jsonData });
   };

   ```

   The `onResolutionChange` listener calls `onOptionsChange` to update the current options with the value from the form field.

### Use the option[​](#use-the-option "Direct link to Use the option")

1. Create a property called `resolution` to the `DataSource` class.

   src/datasource.ts

   ```ts
   export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
     resolution: number;

     constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
       super(instanceSettings);

       this.resolution = instanceSettings.jsonData.resolution || 1000.0;
     }

     // ...

   ```

2. In the `query` method, use the `resolution` property to change how we calculate the step size.

   src/datasource.ts

   ```ts
   const step = duration / this.resolution;

   ```

3. Try it out by configuring a new datasource and changing the value for the resolution.

## Summary[​](#summary "Direct link to Summary")

In this tutorial:

* You built a complete data source plugin for Grafana that uses a query editor to control the data to visualize.
* You added a data source option, commonly used to set connection options and more.

## Learn more[​](#learn-more "Direct link to Learn more")

### Anatomy of a plugin[​](#anatomy-of-a-plugin "Direct link to Anatomy of a plugin")

Every plugin you create requires at least two files: `plugin.json` and `src/module.ts`.

### `plugin.json`[​](#pluginjson "Direct link to pluginjson")

When Grafana starts, it scans the [plugin directory](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#plugins) for any subdirectory that contains a `plugin.json` file. The `plugin.json` file contains information about your plugin and tells Grafana about what capabilities and dependencies your plugin needs.

While certain plugin types can have specific configuration options, let's look at the mandatory ones:

* `type` tells Grafana what type of plugin to expect. Grafana supports three types of plugins: `panel`, `datasource`, and `app`.
* `name` is what users will see in the list of plugins. If you're creating a data source, this is typically the name of the database it connects to, such as Prometheus, PostgreSQL, or Stackdriver.
* `id` uniquely identifies your plugin and should follow this naming convention: `<$organization-name>-<$plugin-name>-<$plugin-type>`. The create-plugin tool correctly configures this based on your responses to its prompts.

To see all the available configuration settings for the `plugin.json`, refer to the [plugin.json Schema](https://grafana.com/developers/plugin-tools/reference/plugin-json.md).

### `module.ts`[​](#modulets "Direct link to modulets")

After discovering your plugin, Grafana loads the `module.js` file, the entrypoint for your plugin. `module.js` exposes the implementation of your plugin, which depends on the type of plugin you're building.

Specifically, `src/module.ts` needs to export a class that extends [GrafanaPlugin](https://github.com/grafana/grafana/blob/f900098cc9f5771c02b6189ba5138547b4f5e6c2/packages/grafana-data/src/types/plugin.ts#L175), and can be any of the following:

* [PanelPlugin](https://github.com/grafana/grafana/blob/f900098cc9f5771c02b6189ba5138547b4f5e6c2/packages/grafana-data/src/panel/PanelPlugin.ts#L95)
* [DataSourcePlugin](https://github.com/grafana/grafana/blob/f900098cc9f5771c02b6189ba5138547b4f5e6c2/packages/grafana-data/src/types/datasource.ts#L33)
* [AppPlugin](https://github.com/grafana/grafana/blob/f900098cc9f5771c02b6189ba5138547b4f5e6c2/packages/grafana-data/src/types/app.ts#L58)

### Data source plugins[​](#data-source-plugins "Direct link to Data source plugins")

A data source in Grafana must extend the `DataSourceApi` interface, which requires you to define two methods: `query` and `testDatasource`.

#### The `query` method[​](#the-query-method "Direct link to the-query-method")

The `query` method is the heart of any data source plugin. It accepts a query from the user, retrieves the data from an external database, and returns the data in a format that Grafana recognizes.

```ts
async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse>

```

The `options` object contains the queries, or *targets*, that the user made, along with context information, like the current time interval. Use this information to query an external database.

#### Test your data source[​](#test-your-data-source "Direct link to Test your data source")

`testDatasource` implements a health check for your data source. For example, Grafana calls this method whenever the user clicks the **Save & Test** button, after changing the connection settings.

```ts
async testDatasource()

```

### Get data from an external API[​](#get-data-from-an-external-api "Direct link to Get data from an external API")

The majority of data sources in Grafana will return data from an external API. This tutorial tries to keep things simple and doesn't require an additional service.

This sample shows the use of the [`getBackendSrv` function](https://github.com/grafana/grafana/blob/main/packages/grafana-runtime/src/services/backendSrv.ts) from the [`grafana-runtime` package](https://github.com/grafana/grafana/tree/main/packages/grafana-runtime).

While you can use something like [axios](https://github.com/axios/axios) or the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to make requests, we recommend using `getBackendSrv` as it proxies requests through the Grafana server rather making the request from the browser. We strongly recommend this when making authenticated requests to an external API. For more information on authenticating external requests, refer to [Add authentication for data source plugins](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-authentication-for-data-source-plugins.md).

### Improve your plugin's quality[​](#improve-your-plugins-quality "Direct link to Improve your plugin's quality")

To learn more about advanced plugin development topics, refer to the following guides:

* [Add support for variables](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-support-for-variables.md)
* [Add support for annotations](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-support-for-annotation-queries.md)
* [Add support for Explore queries](https://grafana.com/developers/plugin-tools/how-to-guides/data-source-plugins/add-features-for-explore-queries.md)
* [Build a logs data source](https://grafana.com/developers/plugin-tools/tutorials/build-a-logs-data-source-plugin.md)
