@frontside/backstage-plugin-graphql-backend v0.1.8
GraphQL Backend
The graphql-backend
plugin adds a GraphQL endpoint
(/api/graphql
) to your Backstage instances, and provides a mechanism
to customize it without having to write any bespoke TypeScript.
It uses GraphQL Modules and Envelop plugins so you can compose pieces of schema and middleware from many different places (including other plugins) into a single, complete GraphQL server.
At a minimum, you should install the graphql-catalog which adds basic schema elements to access the Backstage Catalog via GraphQL
- Getting started
- GraphQL Modules
- Extending Schema
- Envelop Plugins
- GraphQL Context
- Custom Data loaders
- Integrations
Getting Started
To install the GraphQL Backend onto your server:
- Add GraphQL plugin and Application backend module in
packages/backend/src/index.ts
:
import { graphqlPlugin } from '@frontside/backstage-plugin-graphql-backend';
const backend = createBackend();
// GraphQL
backend.use(graphqlPlugin());
- Start the backend
yarn workspace example-backend start
This will launch the full example backend. However, without any modules installed, you won't be able to do much with it.
GraphQL Modules
The way to add new types and new resolvers to your GraphQL backend is with GraphQL Modules. These are portable little bundles of schema that you can drop into place and have them extend your GraphQL server. The most important of these that is maintained by the Backstage team is the graphql-catalog module that makes your Catalog accessible via GraphQL. Add this module to your backend:
// packages/backend/src/index.ts
import { graphqlModuleCatalog } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
const backend = createBackend();
// GraphQL
backend.use(graphqlPlugin());
backend.use(graphqlModuleCatalog());
To learn more about adding your own modules, see the graphql-common package.
Envelop Plugins
Whereas Graphql Modules are used to extend the schema and resolvers of your GraphQL server, Envelop plugins are used to extend its GraphQL stack with tracing, error handling, context extensions, and other middlewares.
Plugins are be added via declaring GraphQL Yoga backend module.
For example, to prevent potentially sensitive error messages from
leaking to your client in production, add the useMaskedErrors
package.
// packages/backend/src/modules/graphqlPlugins.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { graphqlPluginsExtensionPoint } from '@frontside/backstage-plugin-graphql-backend-node';
import { useMaskedErrors } from '@envelop/core';
export const graphqlModulePlugins = createBackendModule({
pluginId: 'graphql',
moduleId: 'plugins',
register(env) {
env.registerInit({
deps: { plugins: graphqlPluginsExtensionPoint },
async init({ plugins }) {
plugins.addPlugins([useMaskedErrors()]);
},
});
},
});
Then add module to your backend:
// packages/backend/src/index.ts
import { graphqlModulePlugins } from './modules/graphqlPlugins';
const backend = createBackend();
// GraphQL
backend.use(graphqlPlugin());
backend.use(graphqlModulePlugins());
GraphQL Context
The GraphQL context is an object that is passed to every resolver function. It is a convenient place to store data that is needed by multiple resolvers, such as a database connection or a logger.
You can add additional data to the context to GraphQL Yoga backend module:
// packages/backend/src/modules/graphqlContext.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { graphqlContextExtensionPoint } from '@frontside/backstage-plugin-graphql-backend-node';
export const graphqlModuleContext = createBackendModule({
pluginId: 'graphql',
moduleId: 'context',
register(env) {
env.registerInit({
deps: { context: graphqlContextExtensionPoint },
async init({ context }) {
context.setContext({ myContext: 'Hello World' });
},
});
},
});
Custom Data Loaders (Advanced)
By default, your graphql context will contain a Dataloader
for retrieving
records from the Catalog by their GraphQL ID. Most of the time this is all you
will need. However, sometimes you will need to load data not just from the
Backstage catalog, but from a different data source entirely. To do this, you
will need to pass batch load functions for each data source.
⚠️Caution! If you find yourself wanting to load data directly from a source other than the catalog, first consider the idea of instead just ingesting that data into the catalog, and then using the default data loader. After consideration, If you still want to load data directly from a source other than the Backstage catalog, then proceed with care.
Load functions are to GraphQL Yoga backend module. Each load function is stored under a unique key which is encoded inside node's id as a data source name
// packages/backend/src/modules/graphqlLoaders.ts
import { createBackendModule } from '@backstage/backend-plugin-api';
import { graphqlLoadersExtensionPoint } from '@frontside/backstage-plugin-graphql-backend-node';
export const graphqlModuleLoaders = createBackendModule({
pluginId: 'graphql',
moduleId: 'loaders',
register(env) {
env.registerInit({
deps: { loaders: graphqlLoadersExtensionPoint },
async init({ loaders }) {
loaders.addLoaders({
ProjectAPI: async (
keys: readonly string[],
context: GraphQLContext,
) => {
/* Fetch */
},
TaskAPI: async (keys: readonly string[], context: GraphQLContext) => {
/* Fetch */
},
});
},
});
},
});
Then you can use @resolve
directive in your GraphQL schemas
interface Node
@discriminates(with: "__source")
@discriminationAlias(value: "Project", type: "ProjectAPI")
@discriminationAlias(value: "Tasks", type: "TaskAPI")
type Project @implements(interface "Node") {
tasks: Tasks @resolve(at: "spec.projectId", from: "TaskAPI")
}
type Tasks @implements(interface "Node") {
list: [Task!] @field(at: "tasks")
}
Integrations
Backstage GraphiQL Plugin
It's convenient to be able to query the Backstage GraphQL API from inside of Backstage App. You can accomplish this by installing the Backstage GraphiQL Plugin and adding the GraphQL API endpoint to the GraphiQL Plugin API factory.
- Once you installed
@backstage/plugin-graphiql
plugin with these instructions - Modify
packages/app/src/apis.ts
to add your GraphQL API as an endpoint
factory: ({ errorApi, githubAuthApi, discovery }) =>
GraphQLEndpoints.from([
{
id: 'backstage-backend',
title: 'Backstage GraphQL API',
// we use the lower level object with a fetcher function
// as we need to `await` the backend url for the graphql plugin
fetcher: async (params: any) => {
const graphqlURL = await discovery.getBaseUrl('graphql');
return fetch(graphqlURL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(params),
}).then(res => res.json());
},
},
]);
Checkout this example packages/app/src/apis.ts
.
Backstage API Docs
You might want to show the schema from your GraphQL API in the API definition section of an API entity in Backstage. You can use the /api/graphql/schema
endpoint to read the schema provided by your GraphQL API. Here's how:
Create the API entity and reference
definition.$text: http://localhost:7007/api/graphql/schema
apiVersion: backstage.io/v1alpha1 kind: API metadata: name: backstage-graphql-api description: GraphQL API provided by GraphQL Plugin spec: type: graphql owner: engineering@backstage.io lifecycle: production definition: $text: http://localhost:7007/api/graphql/schema
Modify
app-config.yaml
to allow reading urls fromlocalhost:7007
backend:
...
reading:
allow:
- host: localhost:7007
13 days ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
5 months ago
7 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago