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
# 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.
// 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>
);
Add a sidebar link so users can find it
Edit packages/app/src/components/Root/Root.tsx. Add a SidebarItem that points to the route.
// 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.
// 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.
// 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.
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.