SuperAPI AI Directory

using-backstage-backend-system

Coding & development

View repository

Updated 10-16 02:23

About this skill

Backstage Backend System Patterns

When to use this skill

  • Defining a new backend plugin (src/plugin.ts)
  • Defining a backend module (src/module.ts) that extends another plugin
  • Choosing between a full plugin vs a module
  • Exposing extension points so other modules can extend your plugin

Choose the right pattern

Building... Pattern Reference
The plugin itself createBackendPlugin({ pluginId, register }) references/plugin-definition.md
A module that extends another plugin createBackendModule({ pluginId, moduleId, register }) references/modules.md
An extension point so other modules can extend yours createExtensionPoint<T>({ id }) references/extension-points.md

Two invocation modes

  • Standalone: editing an existing backend plugin/module.
  • Post-scaffold (called by creating-backstage-plugin for backend-plugin, backend-plugin-module, catalog-provider-module, scaffolder-backend-module): wire up the canonical structure (src/plugin.ts or src/module.ts, src/index.ts re-export).

Common gotchas

  • All wiring imports come from @backstage/backend-plugin-api, not @backstage/core-plugin-api (which is frontend-only).
  • The register(env) callback receives an environment object with env.registerInit({ deps, async init }) and env.registerExtensionPoint(point, impl). deps is a map of service refs the init function needs; init receives the resolved instances.
  • registerInit may be called only once per plugin/module. registerExtensionPoint must be called before registerInit.
  • Plugin-scoped vs root-scoped services: most refs (logger, httpRouter, database, scheduler, auth, httpAuth, userInfo, cache, discovery, urlReader, lifecycle, permissions) are plugin-scoped — each plugin gets its own instance with plugin-aware behavior (e.g. logger pre-tagged with pluginId, database giving a per-plugin schema). Six refs (rootConfig, rootLogger, rootLifecycle, rootHealth, rootHttpRouter, rootInstanceMetadata) are root-scoped — single shared instance. Both can be depended on from a plugin's register block; the distinction matters for behavior, not for injection syntax.
  • A plugin's pluginId and a module's moduleId must match the pattern [a-z][a-z0-9-]* (letters, digits, dashes; starts with a letter).
  • Modules depend on the extension points exported by the target plugin's library package (e.g. @backstage/plugin-catalog-node), not on the plugin's main package.