creating-backstage-catalog-provider
About creating-backstage-catalog-provider
creating-backstage-catalog-provider is a skill for building Backstage catalog providers that periodically ingest entities from external sources. It covers choosing normal or incremental ingestion based on data scale, plus provider and module registration.
Key features
- 01Choose normal or incremental providers
- 02Implement entity ingestion logic
- 03Register the backend module
- 04Configure scheduled refresh or paginated ingestion
Use cases
- 01Ingest catalog entities from an external system
- 02Build paginated ingestion for a large catalog
- 03Add a provider to an existing catalog-backend-module
- 04Set up a provider class and module registration
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ vinzscam/ backstage-skills --skill creating-backstage-catalog-providerAfter installing, type/creating-backstage-catalog-providerin Claude Code to confirm it loaded.
Bundled filesreferences/entity-provider.mdreferences/module-registration.md
Details
From the project READMECreating a Backstage Catalog Provider
When to use this skill
- Building a module that ingests catalog entities from an external source (Azure DevOps, GitHub topic search, internal asset registry, JSON file, etc.)
- Editing an existing
catalog-backend-module-*plugin - The ingestion runs periodically (provider model) — for one-shot processing of an entity definition file, write a
CatalogProcessorinstead
What you build
A catalog provider is a backend module (createBackendModule({ pluginId: 'catalog', ... })) that registers a provider against an extension point. There are two provider models — pick based on data volume.
Choose the model first: normal vs incremental
Use normal EntityProvider when... | Use IncrementalEntityProvider when... |
|---|---|
| Total entity count is small (~hundreds at most) | Total entity count is large (thousands+) |
| The full set fits in memory comfortably | Holding the full set in memory is impractical |
| The source returns the full snapshot in one round-trip (or a handful) | The source supports pagination — cursor / page-token / offset / etc. |
| You're prototyping or building a POC | You're shipping for production-scale catalogs |
| Refresh frequency is low | High write rate or scale; incremental handles batching, backoff, retry |
The two models register against different extension points and have different runtime semantics:
- Normal registers against
catalogProcessingExtensionPointfrom@backstage/plugin-catalog-node. Adopters need no extra install. - Incremental registers against
incrementalIngestionProvidersExtensionPointfrom@backstage/plugin-catalog-backend-module-incremental-ingestion. Adopters must also installcatalogModuleIncrementalIngestionEntityProvider(the framework module that orchestrates batches and persists cursors via the catalog database).
If you don't know upfront, default to normal. Migrating to incremental later is mechanical — same domain fetch logic, different framework wiring.
If the data volume is genuinely unknown, ask the user. Rough thresholds: <500 entities → normal is fine; >10,000 → incremental is required; in between → ask about source pagination support and growth trajectory.
Choose the right pattern
| Building... | Reference |
|---|---|
A normal EntityProvider class (default for small/medium volume) | references/entity-provider.md (Normal section) |
An IncrementalEntityProvider class (large volume) | references/entity-provider.md (Incremental section) |
| The module that wires either kind in | references/module-registration.md |
Two invocation modes
- Standalone: editing an existing catalog-backend-module-* plugin.
- Post-scaffold (called by
creating-backstage-pluginforcatalog-provider-module): scaffold both files together — provider class + module registration.
Common gotchas
- The provider's
getProviderName()must be unique across the catalog (same rule for both normal and incremental). Use a stable name — emitted entities are linked to the provider by it; renaming orphans them. - Normal provider:
applyMutation({ type: 'full', entities })replaces the whole set the provider owns.applyMutation({ type: 'delta', added, removed })adds/removes individual entries — useful when you only learn about changes incrementally. Note this is a delta mutation, not the same thing as theIncrementalEntityProvidermodel. - Normal provider: the connection isn't available until
connect()runs — guardthis.connectionagainstundefinedin any method that might run before connect. - Normal provider: don't fetch from an external system in
connect()directly — receive aSchedulerServiceTaskRunnerand callthis.schedule.run({ id, fn })insideconnect()so the work runs periodically and survives restarts. - Incremental provider: the framework owns the iteration loop. Your
next(context, cursor?)returns one page; the framework calls it repeatedly with the cursor you returned previously, and persists state in the catalog database. Don't try to schedule your own refresh. - Incremental provider: adopters MUST install
catalogModuleIncrementalIngestionEntityProvider(from@backstage/plugin-catalog-backend-module-incremental-ingestion) in addition to your module. Document this prerequisite in your module's README. catalogProcessingExtensionPointcomes from@backstage/plugin-catalog-node(the library package).incrementalIngestionProvidersExtensionPointcomes from@backstage/plugin-catalog-backend-module-incremental-ingestion. Don't depend on@backstage/plugin-catalog-backendfrom a module — that's the host plugin.