How to migrate from BackendSrv.datasourceRequest() to BackendSrv.fetch()

If you’re building a data source plugin, chances are you want to make HTTP requests from your TypeScript code to a HTTP API somewhere.

In the past, the preferred way to make an HTTP request from your plugin was to use the BackendSrv.datasourceRequest(), which has been deprecated in favor of BackendSrv.fetch().

If you tried to simply rename all calls to datasourceRequest to fetch, you’d see an error that looks something like this:

Property 'data' does not exist on type 'Observable<FetchResponse<any>>'.

The reason is because fetch returns an Observable instead of a Promise. This is part of an ongoing effort by the Grafana team to introduce RxJS throughout the Grafana code base. In other words, you might be seeing more of these in the future.

Let’s look at an example that uses datasourceRequest:

import { getBackendSrv } from '@grafana/runtime';

interface ApiResponse {
  namespaces: string[];
}

function async getNamespaces(): Promise<string[]> {
  const response = await getBackendSrv().datasourceRequest<ApiResponse>({
    url: `https://api.example.com/namespaces`,
  });

  return response.data.namespaces;
}

The quick and dirty way to switch to fetch is to use the lastValueFrom() function to convert the Observable to a Promise:

import { getBackendSrv } from '@grafana/runtime';
import { lastValueFrom } from 'rxjs';

interface ApiResponse {
  namespaces: string[];
}

function async getNamespaces(): Promise<string[]> {
  const observableResponse = getBackendSrv()
    .fetch<ApiResponse>({
      url: `https://api.example.com/namespaces`,
    })

  const response = await lastValueFrom(observableResponse);

  return response.data.namespaces;
}

When you’re ready, the next step is to actually start replacing your Promises with Observables, like in the following example:

import { getBackendSrv } from '@grafana/runtime';
import { map } from 'rxjs/operators';

interface ApiResponse {
  namespaces: string[];
}

function getNamespaces(): Observable<string[]> {
  return getBackendSrv()
    .fetch<ApiResponse>({
      url: `https://api.example.com/namespaces`,
    })
    .pipe(
      map((response) => response.data.namespaces)
    );
}

To get the values from the Observable, use Observable.subscribe():

const subscriber = getNamespaces().subscribe((namespaces) => {
  console.log(namespaces);
});

Remember to clean up the subscription when you’re done to avoid memory-leaks:

subscriber.unsubscribe();
2 Likes