GraphQL Voyager logo

Backstage GraphQL Voyager Plugin

Created by PostNL

GraphQL Voyager lets you see a GraphQL schema as a connected map. Types become nodes. Fields and relations become edges. You can pan and zoom. You can follow links to drill into a type. It is a fast way to learn a schema and spot relationships that are hard to see in raw SDL.

The GraphQL Voyager Backstage plugin brings that map into your developer portal. It adds a page where engineers can switch between multiple GraphQL endpoints in one view. That helps when you have separate schemas for environments or services. The plugin uses introspection to fetch a schema, then renders an interactive graph inside Backstage. It supports extra props to tune what Voyager shows, like hiding docs or leaf fields.

Common use cases include onboarding to a new API, exploring third party schemas, checking the impact of a field change, or tracing how a type flows through your domain. Platform teams can expose curated endpoints so folks do not hunt for URLs or tokens. The plugin was proposed and built to make schema exploration a first class tool in Backstage, with support for configuring many endpoints through a simple API provided by the plugin.

If your teams work with GraphQL a lot, this page becomes a shared place to understand how everything connects.

Installation Instructions

These instructions apply to self-hosted Backstage only.

Install the package

Copy
# from your Backstage root directory
yarn --cwd packages/app add @backstage-community/plugin-graphql-voyager

Add the page route in App.tsx

Edit packages/app/src/App.tsx. Import the page component. Add a route so the page is reachable.

Copy
// packages/app/src/App.tsx

import React from 'react';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router-dom';
import { GraphQLVoyagerPage } from '@backstage-community/plugin-graphql-voyager';
// keep your other imports

export const App = () => (
  <AppProvider>
    <AppRouter>
      <Root>
        <FlatRoutes>
          {/* your existing routes */}
          <Route
            path="/graphql-voyager"
            element={<GraphQLVoyagerPage title="GraphQL Voyager" />}
          />
        </FlatRoutes>
      </Root>
    </AppRouter>
  </AppProvider>
);

Edit packages/app/src/components/Root/Root.tsx. Add a SidebarItem that points to the route.

Copy
// packages/app/src/components/Root/Root.tsx

import React from 'react';
import {
  SidebarPage,
  Sidebar,
  SidebarGroup,
  SidebarItem,
} from '@backstage/core-components';
import MapIcon from '@mui/icons-material/Map';
// keep your other imports

export const Root = ({ children }: { children?: React.ReactNode }) => (
  <SidebarPage>
    <Sidebar>
      {/* your existing sidebar groups and items */}
      <SidebarGroup label="Tools">
        <SidebarItem
          icon={MapIcon}
          to="/graphql-voyager"
          text="GraphQL Voyager"
        />
      </SidebarGroup>
    </Sidebar>
    {children}
  </SidebarPage>
);

Provide endpoints through the app APIs

The page needs one or more GraphQL endpoints. You provide them through the app api factory.

Edit packages/app/src/apis.ts. Import the api ref and the endpoints helper. Create a factory that returns your endpoints.

Copy
// packages/app/src/apis.ts

import {
  AnyApiFactory,
  createApiFactory,
  identityApiRef,
} from '@backstage/core-plugin-api';

import {
  graphQlVoyagerApiRef,
  GraphQLVoyagerEndpoints,
} from '@backstage-community/plugin-graphql-voyager';

export const apis: AnyApiFactory[] = [
  createApiFactory({
    api: graphQlVoyagerApiRef,
    deps: { identityApi: identityApiRef },
    factory: ({ identityApi }) => {
      return GraphQLVoyagerEndpoints.from([
        {
          id: 'main-graphql',
          title: 'Main GraphQL',
          introspectionErrorMessage:
            'Unable to perform introspection. Check your auth or endpoint.',
          introspection: async (query: any) => {
            const { token } = await identityApi.getCredentials();

            const res = await fetch('https://your.graphql.endpoint/graphql', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${token}`,
              },
              body: JSON.stringify({ query }),
            });

            return res.json();
          },
          voyagerProps: {
            hideDocs: false,
          },
        },
        // add more endpoints if you need to switch between them in the UI
        // {
        //   id: 'secondary-graphql',
        //   title: 'Secondary GraphQL',
        //   introspectionErrorMessage: 'Could not introspect secondary service.',
        //   introspection: async (query: any) => {
        //     const res = await fetch('https://secondary.example.com/graphql', {
        //       method: 'POST',
        //       headers: { 'Content-Type': 'application/json' },
        //       body: JSON.stringify({ query }),
        //     });
        //     return res.json();
        //   },
        //   voyagerProps: { hideDocs: true },
        // },
      ]);
    },
  }),
];

Notes on the config

  • The plugin calls your introspection function with a query string. Return the JSON from your GraphQL server.
  • Set voyagerProps to tweak the viewer. For example hideDocs true to hide the right side docs pane.

Optional use of the Router export

You can render the Router export instead of the page export. Both take the same props.

Copy
// packages/app/src/App.tsx

import { Router as GraphQLVoyagerRouter } from '@backstage-community/plugin-graphql-voyager';

<FlatRoutes>
  <Route
    path="/graphql-voyager"
    element={<GraphQLVoyagerRouter title="GraphQL Voyager" />}
  />
</FlatRoutes>

Changelog

This changelog is produced from commits made to the GraphQL Voyager plugin since a year ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.

Maintenance

  • Remove unused canvas dev dependency in the GraphQL Voyager plugin. Install size is smaller for consumers. #3565 merged 6 months ago

  • Update repo tools config to reduce knip false positives for this plugin. No package release. #3018 merged 7 months ago

Set up Backstage in minutes with Roadie