SuperAPI AI Directory

creating-backstage-scaffolder-action

Deployment & operations

View repository

Updated 10-16 02:21

About this skill

Creating a Backstage Scaffolder Action

When to use this skill

  • Building a custom action that scaffolder templates can call (e.g. acme:user:create, myorg:slack:notify)
  • Editing an existing scaffolder-backend-module-* plugin

What you build

A scaffolder action is a backend module (createBackendModule({ pluginId: 'scaffolder', ... })) that registers a TemplateAction against the scaffolderActionsExtensionPoint. Two pieces:

  1. The action itself (created via createTemplateAction)
  2. The module that registers it

Choose the right pattern

Building... Reference
The action itself references/template-action.md
The module that wires it in references/module-registration.md

Two invocation modes

  • Standalone: editing an existing scaffolder-backend-module-*.
  • Post-scaffold (called by creating-backstage-plugin for scaffolder-backend-module): scaffold both files together — action definition + module registration.

Common gotchas

  • Action id follows the <namespace>:<name> (or <namespace>:<group>:<name>) convention. The namespace is yours to choose; pick something stable that won't collide with other actions in the catalog of installed modules.
  • Schemas are factory functions, not raw Zod schemas: input: { name: z => z.string() } (not input: { name: z.string() }). Backstage uses this pattern to pin its Zod version internally — verify the form against ../backstage/plugins/scaffolder-node/src/actions/createTemplateAction.ts.
  • Action handler context (ctx) provides: ctx.logger, ctx.workspacePath, ctx.input (typed from the input schema), ctx.output(name, value) to emit outputs, ctx.getInitiatorCredentials() for the user identity, ctx.createTemporaryDirectory() for sandboxed work, ctx.signal (AbortSignal) for cancellation, ctx.isDryRun for dry-run mode.
  • Long-running actions should call ctx.logger.info periodically — the scaffolder UI streams logs to the user.
  • For dry-run support, set supportsDryRun: true and check ctx.isDryRun in the handler to skip side effects.
  • Templates can use if: ${{ always() }} and if: ${{ failure() }} to control step execution after failures — always() runs unconditionally, failure() runs only when a prior step failed. These are built into the template engine; action authors don't need to do anything to support them.