Module 00 · Orientation · 20 min · You are: Platform and product
Orientation: how a Backstage plugin is built
In this module
- You learn: Plugins, modules, services and extension points in the backend; plugins, extensions and blueprints in the frontend; and why one plugin is four or more packages.
- You do: Read three files from the reference repo, check your tools, and start the portal you finished in the first course.
- You end with: Your portal runs on your laptop with Postgres, the catalog knows
checkout, and the reference repo is up to date.
In the first course you configured Backstage from the outside. Now you write code that runs inside it. Backstage gives plugin code a small set of building blocks with strict rules: a backend plugin only gets what it asks for, and a frontend plugin is a list of extensions the app plugs in. Learn the blocks first and every plugin you read, including the official ones, looks the same.
Open this module in the interactive course to track progress.
Concepts
- Backend plugin
- One feature with its own API under
/api/<pluginId>and its own database. It declares the services it needs (logger, database, scheduler) and gets them injected. Backend system. - Module and extension point
- A module adds to one plugin without changing its code, through an extension point the plugin offers. Your platform team owns the plugin; other teams own modules.
- Frontend plugin
- A list of extensions: a page, an API client, an entity card. Each is made from a blueprint and attaches to a place in the app. Frontend system.
- Package roles
readiness-backend(the server),readiness(the UI),readiness-common(types and permissions both use),readiness-node(what backend modules import). Plugin structure.- The readiness plugin
- A production-readiness scorecard: each component gets checks (has an owner, has docs, links a runbook) and a score. Teams see it on their entity page and add their own checks.
Who owns what in this course
| Thing | Package | Owner |
|---|---|---|
| The readiness API, storage and schedule | plugins/readiness-backend | Platform team |
| Shared types and permissions | plugins/readiness-common | Platform team |
| The extension point for new checks | plugins/readiness-node | Platform team |
| The page, card and tab | plugins/readiness | Platform team |
| A check only team-checkout needs | plugins/readiness-backend-module-api | team-checkout |
| Who may evaluate | plugins/permission-backend-module-acme | Platform team |
Everything lives in the portal repo. A team that owns a module owns its folder and reviews its changes (a CODEOWNERS line in a real repo).
Files to know
Where you are heading. Click Quick look on each file: it opens the finished reference file with numbered circles on the lines that matter.
Open them in your editor or on GitHub; the bullets say what to look at.
README.md(reference, your repo): The map of the plugin packages.- Seven packages, one plugin L35–47
portal/packages/backend/src/index.ts(reference, your repo): The backend: where plugins and modules are added.portal/plugins/readiness-backend/package.json(reference, your repo): A backend plugin is a package with a role.
Steps
1. Tell the course who you are
The same GitHub user and folder as in the first course. If you set them there, they are already filled in.
Set your GitHub user and workspace folder in the interactive course or the form at the top of the notes index; these notes then show your values.
2. Check your tools
Each tool reports ok, MISSING or PROBLEM. Fix what it reports and run it again until it says All set.
check() { # tool, works-check, version, problem, where-to-get-it, optional
if ! command -v "$1" >/dev/null 2>&1; then
if [ -n "$6" ]; then printf ' optional %-22s not installed (%s)\n' "$1" "$5"
else printf ' MISSING %-22s install it: %s\n' "$1" "$5"; bad=$((bad+1)); fi
elif ! eval "$2" >/dev/null 2>&1; then printf ' PROBLEM %-22s %s\n' "$1" "$4"; [ -n "$6" ] || bad=$((bad+1))
else printf ' ok %-22s %s\n' "$1" "$(eval "$3" 2>&1 | head -n 1)"; fi
}
bad=0
check 'git' 'git --version' 'git --version' 'installed, but it does not run' 'https://git-scm.com/downloads'
check 'node' "node -v | grep -Eq '^v(22|24)[.]'" 'node -v' 'found, but Backstage 1.56 needs Node.js 22 or 24' 'https://nodejs.org/en/download'
check 'yarn' 'yarn --version' 'yarn --version' 'installed, but it does not run' 'https://yarnpkg.com/getting-started/install (or: corepack enable)'
check 'docker' 'docker info' "docker version --format '{{.Server.Version}}'" 'installed, but the Docker daemon is not running: start Docker Desktop (or the docker service)' 'https://docs.docker.com/get-docker/'
check 'gh' 'gh auth status' 'gh --version' 'installed, but not logged in: run gh auth login' 'https://cli.github.com/'
check 'jq' 'jq --version' 'jq --version' 'installed, but it does not run' 'https://jqlang.org/download/'
check 'curl' 'curl --version' 'curl --version' 'installed, but it does not run' 'your package manager (it ships with macOS and most Linux distributions)'
if [ "$bad" -eq 0 ]; then echo "All set: every tool is ready."; else echo "$bad tool(s) need attention. Install or fix them, then run this check again."; fi
3. Update the reference repo
You copy a few long files from it. Pull the latest, or clone it if you no longer have it.
git -C ~/work/backstage-workshop pull -q || git clone -q https://github.com/platformengr/backstage-workshop.git ~/work/backstage-workshop
The plugin packages are in portal/plugins. Expect readiness, readiness-backend and five more:
ls ~/work/backstage-workshop/portal/plugins
4. Start your portal
This course builds on the portal repo from the first course. Start Postgres and the portal the way you left them.
docker start portal-db
cd ~/work/portal && git switch -q main && git pull -q && yarn install
Warning. This terminal is the portal. Leave it running. Use a second terminal for everything else.
cd ~/work/portal && set -a && . ./.env && set +a && yarn start
Sign in with GitHub. In a second terminal, check that the catalog knows checkout and its owner. Expect team-checkout:
cd ~/work/portal && set -a && . ./.env && set +a
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" localhost:7007/api/catalog/entities/by-name/component/default/checkout | jq -r .spec.owner
Simplified for the course
All packages live in the
portalrepo. Larger companies publish shared plugins from their own repos to a private npm registry.
Quiz
- team-checkout needs one extra readiness check. Where does their code go?
- A backend module that adds the check through the plugin's extension point
- A pull request that edits the readiness plugin's list of checks directly
- A second copy of the readiness backend plugin with the extra check added
- Which package holds the types both the frontend and the backend use?
readiness-backend, because it creates the data the frontend showsreadiness-common, which both sides can importreadiness-node, because types are compiled by Node
- How does a backend plugin get a database connection?
- It reads the Postgres password from
app-config.yamland connects itself - It imports the database client from the catalog plugin
- It declares the database service as a dependency and gets one injected
- It reads the Postgres password from
Answers
- A backend module that adds the check through the plugin's extension point Modules extend a plugin without changing it, so each team owns its own code.
readiness-common, which both sides can import A frontend plugin must never import a backend package;-commonis safe for both.- It declares the database service as a dependency and gets one injected Services are injected. The backend decides how they are built; the plugin only asks.
Field note
When I review plugin code, the first thing I look at is the list of services in registerInit. It tells me in ten lines what the plugin can touch: the database, the catalog, the network. Keep that list honest.