A frontend library that helps other Backstage plugins interact with the catalog through React components, hooks, and utilities.
—
Helper functions for entity relationships, source location, ownership checks, and route management. These utilities provide common functionality for working with Backstage entities.
Functions for discovering and working with entity relationships in the catalog.
/**
* Gets related entities based on relationship type and optional filters
* @param entity - Base entity to find relations for
* @param relationType - Type of relationship to look for
* @param filter - Optional filter for related entity kind
* @returns Array of related entity references
*/
function getEntityRelations(
entity: Entity | undefined,
relationType: string,
filter?: { kind: string }
): CompoundEntityRef[];Usage Examples:
import { getEntityRelations } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
function EntityRelations({ entity }: { entity: Entity }) {
// Get all API dependencies
const apiDependencies = getEntityRelations(entity, 'dependsOn', { kind: 'api' });
// Get all systems this component belongs to
const systems = getEntityRelations(entity, 'partOf', { kind: 'system' });
// Get all components that depend on this API
const consumers = getEntityRelations(entity, 'providedBy');
return (
<div>
<h3>API Dependencies</h3>
{apiDependencies.map(ref => (
<EntityRefLink key={ref.name} entityRef={ref} />
))}
<h3>Part of Systems</h3>
{systems.map(ref => (
<EntityRefLink key={ref.name} entityRef={ref} />
))}
<h3>Consumers</h3>
{consumers.map(ref => (
<EntityRefLink key={ref.name} entityRef={ref} />
))}
</div>
);
}
// Find specific relationship types
function ComponentDependencies({ component }: { component: Entity }) {
const dependencies = getEntityRelations(component, 'dependsOn');
const provides = getEntityRelations(component, 'providesApi', { kind: 'api' });
return (
<div>
<p>Depends on {dependencies.length} entities</p>
<p>Provides {provides.length} APIs</p>
</div>
);
}Functions for determining the source location and integration type of entities.
/**
* Gets the source location information for an entity
* @param entity - Entity to get source location for
* @param scmIntegrationsApi - SCM integrations API for URL resolution
* @returns Source location information or undefined if not available
*/
function getEntitySourceLocation(
entity: Entity,
scmIntegrationsApi: ScmIntegrationsApi
): EntitySourceLocation | undefined;
/**
* Information about an entity's source location
*/
interface EntitySourceLocation {
/** Target URL for the source location */
locationTargetUrl: string;
/** Type of integration (github, gitlab, etc.) */
integrationType?: string;
}Usage Examples:
import {
getEntitySourceLocation,
EntitySourceLocation
} from '@backstage/plugin-catalog-react';
import { useApi } from '@backstage/core-plugin-api';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
function EntitySourceInfo({ entity }: { entity: Entity }) {
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
const sourceLocation = getEntitySourceLocation(entity, scmIntegrationsApi);
if (!sourceLocation) {
return <p>No source location available</p>;
}
return (
<div>
<h4>Source Location</h4>
<a
href={sourceLocation.locationTargetUrl}
target="_blank"
rel="noopener noreferrer"
>
View Source
{sourceLocation.integrationType && (
<span> ({sourceLocation.integrationType})</span>
)}
</a>
</div>
);
}
// Check if entity has source location
function hasSourceLocation(entity: Entity, scmIntegrationsApi: ScmIntegrationsApi): boolean {
return getEntitySourceLocation(entity, scmIntegrationsApi) !== undefined;
}
// Get integration type for UI icons
function getIntegrationType(entity: Entity, scmIntegrationsApi: ScmIntegrationsApi): string | undefined {
const location = getEntitySourceLocation(entity, scmIntegrationsApi);
return location?.integrationType;
}Functions for checking entity ownership relationships.
/**
* Checks if one entity owns another entity
* @param owner - Potential owner entity (user or group)
* @param entity - Entity to check ownership of
* @returns True if owner owns the entity
*/
function isOwnerOf(owner: Entity, entity: Entity): boolean;Usage Examples:
import { isOwnerOf } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
function OwnershipCheck({
user,
entities
}: {
user: Entity;
entities: Entity[]
}) {
const ownedEntities = entities.filter(entity => isOwnerOf(user, entity));
return (
<div>
<h3>Entities owned by {user.metadata.name}</h3>
<p>Owns {ownedEntities.length} of {entities.length} entities</p>
{ownedEntities.map(entity => (
<EntityRefLink key={entity.metadata.name} entityRef={entity} />
))}
</div>
);
}
// Check ownership for access control
function EntityActions({ entity, currentUser }: { entity: Entity; currentUser: Entity }) {
const canEdit = isOwnerOf(currentUser, entity);
return (
<div>
{canEdit && (
<button>Edit Entity</button>
)}
<button>View Details</button>
</div>
);
}
// Group ownership check
function GroupEntities({ group, allEntities }: { group: Entity; allEntities: Entity[] }) {
const groupEntities = allEntities.filter(entity => isOwnerOf(group, entity));
return (
<div>
<h3>{group.metadata.name} Group Entities</h3>
<EntityTable
title={`${group.metadata.name} Portfolio`}
entities={groupEntities}
columns={[
{ title: 'Name', field: 'metadata.name' },
{ title: 'Kind', field: 'kind' },
{ title: 'Type', field: 'spec.type' }
]}
/>
</div>
);
}Utilities for managing entity routes and navigation within Backstage.
/**
* Route reference for entity pages with parameters
* Includes parameters: namespace, kind, name
*/
const entityRouteRef: RouteRef<{
namespace: string;
kind: string;
name: string;
}>;
/**
* Extracts route parameters from an entity
* @param entity - Entity to extract parameters from
* @returns Object with kind, namespace, and name parameters
*/
function entityRouteParams(entity: Entity): {
kind: string;
namespace: string;
name: string;
};Usage Examples:
import {
entityRouteRef,
entityRouteParams
} from '@backstage/plugin-catalog-react';
import { useRouteRef } from '@backstage/core-plugin-api';
function EntityNavigation() {
const entityRoute = useRouteRef(entityRouteRef);
const navigateToEntity = (entity: Entity) => {
const params = entityRouteParams(entity);
const url = entityRoute(params);
// Navigate to entity page
window.location.href = url;
};
return null;
}
// Generate entity URLs
function EntityLink({ entity }: { entity: Entity }) {
const entityRoute = useRouteRef(entityRouteRef);
const params = entityRouteParams(entity);
const entityUrl = entityRoute(params);
return (
<a href={entityUrl}>
{entity.metadata.name}
</a>
);
}
// Use in breadcrumbs or navigation
function EntityBreadcrumb({ entity }: { entity: Entity }) {
const params = entityRouteParams(entity);
return (
<div>
<span>{params.namespace}</span> /
<span>{params.kind}</span> /
<strong>{params.name}</strong>
</div>
);
}
// Custom route handling
function EntityRouter({ entities }: { entities: Entity[] }) {
const entityRoute = useRouteRef(entityRouteRef);
const buildEntityMap = () => {
const entityMap = new Map();
entities.forEach(entity => {
const params = entityRouteParams(entity);
const url = entityRoute(params);
entityMap.set(url, entity);
});
return entityMap;
};
const entityUrlMap = buildEntityMap();
return null; // Router implementation
}Internal utility functions for processing and combining entity filters.
/**
* Reduces multiple entity filters into a single catalog filter object
* @param filters - Array of EntityFilter instances to combine
* @returns Combined filter object for catalog backend queries
*/
function reduceCatalogFilters(
filters: EntityFilter[]
): Record<string, string | symbol | (string | symbol)[]>;
/**
* Reduces entity filters to backend-compatible filters only
* @param filters - Array of EntityFilter instances
* @returns Filter object containing only backend-compatible filters
*/
function reduceBackendCatalogFilters(
filters: EntityFilter[]
): Record<string, string | symbol | (string | symbol)[]>;
/**
* Creates a composite filter function from multiple entity filters
* @param filters - Array of EntityFilter instances with filterEntity methods
* @returns Combined filter function that applies all filters
*/
function reduceEntityFilters(
filters: EntityFilter[]
): (entity: Entity) => boolean;Usage Examples:
import {
reduceCatalogFilters,
reduceBackendCatalogFilters,
reduceEntityFilters,
EntityKindFilter,
EntityTypeFilter,
EntityTagFilter
} from '@backstage/plugin-catalog-react';
// Combine multiple filters for backend queries
function CombinedFiltersExample() {
const filters = [
new EntityKindFilter('component'),
new EntityTypeFilter(['service', 'website']),
new EntityTagFilter(['database', 'web'])
];
// Get combined catalog filters for backend
const catalogFilters = reduceCatalogFilters(filters);
// Result: { kind: 'component', 'spec.type': ['service', 'website'], ... }
// Get only backend-compatible filters
const backendFilters = reduceBackendCatalogFilters(filters);
// Create frontend filter function
const filterFunction = reduceEntityFilters(filters);
// Apply to entity list
const filteredEntities = allEntities.filter(filterFunction);
return (
<div>
<p>Applied {filters.length} filters</p>
<p>Found {filteredEntities.length} matching entities</p>
</div>
);
}
// Advanced filter composition
function AdvancedFilterComposition() {
const baseFilters = [
new EntityKindFilter('component'),
new EntityTypeFilter('service')
];
const userFilters = [
new EntityTagFilter(['production']),
// Custom filters that only work frontend-side
];
// Separate backend and frontend filters
const backendQuery = reduceBackendCatalogFilters(baseFilters);
const frontendFilter = reduceEntityFilters([...baseFilters, ...userFilters]);
return { backendQuery, frontendFilter };
}These utilities often work together to provide comprehensive entity management:
import {
getEntityRelations,
getEntitySourceLocation,
isOwnerOf,
entityRouteParams
} from '@backstage/plugin-catalog-react';
import { useApi } from '@backstage/core-plugin-api';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
function EntityDetailsPage({
entity,
currentUser
}: {
entity: Entity;
currentUser: Entity
}) {
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
// Get various entity information
const sourceLocation = getEntitySourceLocation(entity, scmIntegrationsApi);
const dependencies = getEntityRelations(entity, 'dependsOn');
const consumers = getEntityRelations(entity, 'dependsOn').filter(
ref => ref.kind === 'component'
);
const canEdit = isOwnerOf(currentUser, entity);
const routeParams = entityRouteParams(entity);
return (
<div>
<h1>{entity.metadata.name}</h1>
{/* Route information */}
<p>
{routeParams.namespace}/{routeParams.kind}/{routeParams.name}
</p>
{/* Source location */}
{sourceLocation && (
<a href={sourceLocation.locationTargetUrl} target="_blank">
View Source ({sourceLocation.integrationType})
</a>
)}
{/* Relationships */}
<h3>Dependencies ({dependencies.length})</h3>
{dependencies.map(dep => (
<EntityRefLink key={dep.name} entityRef={dep} />
))}
<h3>Consumers ({consumers.length})</h3>
{consumers.map(consumer => (
<EntityRefLink key={consumer.name} entityRef={consumer} />
))}
{/* Actions based on ownership */}
{canEdit && (
<div>
<button>Edit Entity</button>
<button>Delete Entity</button>
</div>
)}
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-backstage--plugin-catalog-react