PlatformEngr® Backstage plugins course

Cheat notes · Module 02 · The backend plugin

Database, config and a schedule

A five-minute review for when you have done this module and want it back in your head. Open the full module for the steps and the lab, or the full text to print.

The idea

A stored score with a time on it is something teams can trust. Every plugin that stores data or runs jobs uses the same three services: its own database with Knex migrations, a config schema, and a scheduler that runs a job once across replicas.

Remember

  • Database service: each plugin gets its own database (backstage_plugin_<id>) and a Knex client. Ship migrations in the package (migrations/, run once each in name order) and list migrations in files.
  • Config schema: a config.d.ts named in package.json under configSchema documents your keys; config:check and the backend validate against it.
  • Scheduler: scheduler.scheduleTask({ id, frequency, timeout, initialDelay, fn }). With several replicas, a database lock makes only one run it.
  • Acting as the plugin: a scheduled job has no user. auth.getOwnServiceCredentials() gives the plugin its own credentials to call the catalog.
  • Read config with config.getOptionalStringArray(...), and schedules with readSchedulerServiceTaskScheduleDefinitionFromConfig.

Pitfalls

  • A string where a list belongs (disabledChecks: has-runbook): the plugin fails at startup with got string, wanted string-array and the API answers 503. config:check --strict in CI catches it, but only for keys you described in config.d.ts.
  • No config.d.ts: typos in config are silently ignored and nobody knows which keys exist.

Commands

yarn backstage-cli config:check --strictValidate config against every schema
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" localhost:7007/api/readiness/scorecardsStored scorecards
docker exec portal-db psql -U postgres -d backstage_plugin_readiness -c 'select entity_ref, score from readiness_scorecards'The rows in the plugin's own database

Config and its schema

# app-config.yaml
readiness:
  schedule:
    frequency: { minutes: 30 }
    timeout: { minutes: 5 }
    initialDelay: { seconds: 15 }
  disabledChecks: []

// config.d.ts
export interface Config {
  readiness?: {
    schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
    disabledChecks?: string[];
  };
}

Test yourself

Answer in your head first, then open the answer.

Why does the readiness backend get its own database?

Each plugin owns its tables and migrations, so plugins cannot break each other's data.

Two replicas run the same scheduled task. How often does it run?

Once per interval: a lock in the database picks one replica.

Whose credentials does the scheduled job use to read the catalog?

The plugin's own service credentials.

Where module 02 leaves you: Every 30 minutes the backend scores every component and stores the result in backstage_plugin_readiness. Settings live under readiness: in app-config.yaml. Merged.