A [GraphQL Module][graphql-module] providing access to the Backstage Software Catalog
The plugin provides basic Catalog types, such as Entity
, User
,
Component
, System
, etc... and extends the Directives
API with @relation
directive.
You will almost always want to start by adding this plugin to your Graphql Backend
Some key features are currently missing. These features may change the schema in backward-incompatible ways.
-
filter
query for filteringnodes/entities
. -
viewer
query for retrieving data for the current user.
This package provides two GraphQL modules:
-
Catalog
module – provides basic Catalog GraphQL types and@relation
directive -
Relation
module – provides only@relation
directive
For the Backstage plugin system,
you have to pass Catalog
or Relation
GraphQL module to the modules
option and
create catalog DataLoader
:
import { createRouter } from '@frontside/backstage-plugin-graphql-backend';
import { Catalog } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
// packages/backend/src/plugins/graphql.ts
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
modules: [Catalog],
loaders: { ...createCatalogLoader(env.catalogClient) },
// You might want to pass catalog client to the context
// and use it in resolvers, but it's not required
context: ctx => ({ ...ctx, catalog: env.catalogClient }),
});
}
For the backend system, you can add them as a plugin modules:
- To use
Catalog
GraphQL module
// packages/backend/src/index.ts
import { graphqlModuleCatalog } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
const backend = createBackend();
backend.use(graphqlModuleCatalog());
- To use
Relation
GraphQL module
import { graphqlModuleRelationResolver } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
const backend = createBackend();
backend.use(graphqlModuleRelationResolver());
If you don't want to use basic Catalog types for some reason, but
still want to use @relation
directive, you can install Relation
module
@relation
directive allows you to resolve relationships between
entities. Similar to @field
directive, it writes a resolver for you
so you do not have to write a resolver yourself. It assumes that
relationships are defined as standard Entity
relationships. The
name
argument allows you to specify the type of the relationship. It
will automatically look up the entity in the catalog.
- To define a
User
that is theowner
of aComponent
:
type Component {
owner: User @relation(name: "ownedBy")
}
- The GraphQL server has baked in support for Relay. By
default, collections defined by a
@relation
directive are modeled as arrays. However, if the relationship is large, and should be paginated, you can specify it withConnection
as the field type and use thenodeType
argument to specify what the target of the collection should be.
type Repository {
contributors: Connection @relation(name: "contributedBy", nodeType: "User")
# Or you can just use an array of entities
contributors: [User] @relation(name: "contributedBy")
}
- If you have different kinds of relationships with the same type you
can filter them by
kind
argument:
type System {
components: Connection
@relation(name: "hasPart", nodeType: "Component", kind: "component")
resources: Connection
@relation(name: "hasPart", nodeType: "Resource", kind: "resource")
}
If you need to implement complicated logic for some fields and can't be achieved with available directives, you can write your own resolvers. To do this, you need to define a resolver function in your GraphQL module:
import { createModule } from "graphql-modules";
import type { CatalogClient, QueryEntitiesRequest } from '@backstage/catalog-client';
import { encodeEntityId } from '@frontside/backstage-plugin-graphql-backend-module-catalog';
export const myModule = createModule({
/* ... */
resolvers: {
Task: {
// This resolver utilize 3rd party api to get entity ref and then encodes it to NodeId
// Which will be resolved to an entity
entity: async (_, args, { taskAPI }) => {
const response = await taskAPI.getTask(args.taskId);
return { id: encodeEntityId(response.entityRef) };
},
},
Query: {
// Here you can use catalog client to query entities
entities: async (
_: any,
args: QueryEntitiesRequest,
// If you aren't using Backstage Backend System https://backstage.io/docs/backend-system/
// This requires you to pass catalog client to the context
{ catalog }: { catalog: CatalogClient }
): Promise<{ id: string }[]> => {
const { items: entities } = await catalog.queryEntities(args);
return entities.map(entity => ({ id: encodeEntityId(entity) }));
},
},
},
});