Node.js utilities and types for building Backstage catalog modules, providing core APIs for catalog processors, entity providers, and processing workflows
—
Alpha extension points and service references for plugin-based architecture integration in Backstage. These APIs provide integration points for the new backend plugin system and are marked as alpha, meaning they may change in future versions.
Service reference for accessing the catalog API in the new backend plugin system.
/**
* Service reference for the catalog API
* Provides access to catalog operations within the plugin system
*/
const catalogServiceRef: ServiceRef<CatalogApi, 'plugin'>;Usage Examples:
import { catalogServiceRef } from "@backstage/plugin-catalog-node/alpha";
import { createBackendPlugin } from "@backstage/backend-plugin-api";
export const myPlugin = createBackendPlugin({
pluginId: 'my-plugin',
register(env) {
env.registerInit({
deps: {
catalog: catalogServiceRef,
},
async init({ catalog }) {
// Use the catalog service
const entities = await catalog.getEntities({
filter: { kind: 'Component' }
});
console.log(`Found ${entities.items.length} components`);
},
});
},
});Extension point for registering catalog processors, entity providers, and placeholder resolvers in the new backend plugin system.
/**
* Extension point for catalog processing functionality
* Allows plugins to extend catalog processing capabilities
*/
interface CatalogProcessingExtensionPoint {
/**
* Add one or more catalog processors
* @param processors - Processors to add, can be single processor or array
*/
addProcessor(...processors: Array<CatalogProcessor | Array<CatalogProcessor>>): void;
/**
* Add one or more entity providers
* @param providers - Entity providers to add, can be single provider or array
*/
addEntityProvider(...providers: Array<EntityProvider | Array<EntityProvider>>): void;
/**
* Add a placeholder resolver for template variable resolution
* @param key - The placeholder key to resolve (e.g., 'myorg')
* @param resolver - Function to resolve the placeholder
*/
addPlaceholderResolver(key: string, resolver: PlaceholderResolver): void;
}
/**
* Extension point instance for catalog processing
*/
const catalogProcessingExtensionPoint: ExtensionPoint<CatalogProcessingExtensionPoint>;Usage Examples:
import {
catalogProcessingExtensionPoint,
CatalogProcessor,
EntityProvider,
PlaceholderResolver
} from "@backstage/plugin-catalog-node/alpha";
import { createBackendPlugin } from "@backstage/backend-plugin-api";
// Custom processor example
class MyCustomProcessor implements CatalogProcessor {
getProcessorName(): string {
return "my-custom-processor";
}
async preProcessEntity(entity, location, emit, originLocation, cache) {
// Add custom processing logic
return {
...entity,
metadata: {
...entity.metadata,
annotations: {
...entity.metadata.annotations,
"my-plugin.io/processed": "true"
}
}
};
}
}
// Custom entity provider example
class MyEntityProvider implements EntityProvider {
getProviderName(): string {
return "my-entity-provider";
}
async connect(connection) {
// Provide entities from external source
const entities = await this.fetchExternalEntities();
await connection.applyMutation({
type: "full",
entities: entities.map(entity => ({ entity }))
});
}
private async fetchExternalEntities() {
// Implementation to fetch from external API
return [];
}
}
// Custom placeholder resolver example
const myPlaceholderResolver: PlaceholderResolver = async ({ key, value, read }) => {
if (key === 'myorg') {
return 'my-organization-name';
}
if (key === 'version') {
const packageJson = await read('package.json');
const pkg = JSON.parse(packageJson.toString());
return pkg.version;
}
return value;
};
export const myPlugin = createBackendPlugin({
pluginId: 'my-plugin',
register(env) {
env.registerInit({
deps: {
catalogProcessing: catalogProcessingExtensionPoint,
},
async init({ catalogProcessing }) {
// Register processor
catalogProcessing.addProcessor(new MyCustomProcessor());
// Register entity provider
catalogProcessing.addEntityProvider(new MyEntityProvider());
// Register placeholder resolver
catalogProcessing.addPlaceholderResolver('myorg', myPlaceholderResolver);
catalogProcessing.addPlaceholderResolver('version', myPlaceholderResolver);
// Can also add multiple at once
catalogProcessing.addProcessor([
new MyCustomProcessor(),
new AnotherProcessor()
]);
},
});
},
});The extension point supports flexible registration patterns for different use cases:
import { catalogProcessingExtensionPoint } from "@backstage/plugin-catalog-node/alpha";
// Register processors individually
catalogProcessing.addProcessor(processorA);
catalogProcessing.addProcessor(processorB);
// Register multiple processors at once
catalogProcessing.addProcessor(processorA, processorB, processorC);
// Register array of processors
catalogProcessing.addProcessor([processorA, processorB]);
// Mix of individual and arrays
catalogProcessing.addProcessor(processorA, [processorB, processorC], processorD);
// Same patterns work for entity providers
catalogProcessing.addEntityProvider(providerA, [providerB, providerC]);type ServiceRef<TService, TScope = 'root'> = {
id: string;
scope: TScope;
defaultFactory?: (service: ServiceRef<TService, TScope>) => Promise<ServiceFactory<TService>>;
};
type ExtensionPoint<T> = {
id: string;
T: T;
};
type ServiceFactory<TService> = {
service: ServiceRef<TService>;
initialization: 'always' | 'lazy';
factory: (...args: any[]) => TService | Promise<TService>;
};When migrating from the legacy catalog backend system to the new plugin system:
// Legacy approach
const catalogBuilder = CatalogBuilder.create(env);
catalogBuilder.addProcessor(new MyProcessor());
// New plugin system approach
export const myPlugin = createBackendPlugin({
register(env) {
env.registerInit({
deps: {
catalogProcessing: catalogProcessingExtensionPoint,
},
async init({ catalogProcessing }) {
catalogProcessing.addProcessor(new MyProcessor());
},
});
},
});Install with Tessl CLI
npx tessl i tessl/npm-backstage--plugin-catalog-node