Cheat notes · Module 01 · The backend plugin
A backend plugin with an API
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 backend plugin is an id plus registerInit: declare the services you need, then mount a router. Every route is authenticated by default, and calling the catalog with the caller's credentials means you never show what the caller could not see.
Remember
createBackendPlugin:pluginIdandregister(env)→env.registerInit({ deps, init }).initruns once at startup.httpRouter: mounts your Express router at/api/<pluginId>. The id alone decides the path.- Auth by default: every route needs credentials (a user, another plugin, a configured token).
httpAuth.credentials(req)says who called. - Calling the catalog:
catalogServiceRefis the catalog client as a service. Pass{ credentials }and the catalog applies its own permissions. - Errors: throw
NotFoundErrororInputErrorfrom@backstage/errors; the backend answers 404 or 400 with a JSON body. Validate bodies with Zod. - Generate, don't hand-roll:
yarn new --select backend-plugincreates the package and adds thebackend.addline;plugin-common-librarymakes-common.
Pitfalls
- Losing the
backend.addline in a merge conflict: the backend starts without errors and your API answers404with an empty body. Smoke-test one route per plugin after deploy: 200 or 401, never 404. - Reading the catalog with the plugin's own credentials in a user request: you could leak entities the user may not see.
Commands
yarn new --select plugin-common-library --option pluginId=readiness | The shared types package |
yarn new --select backend-plugin --option pluginId=readiness | The backend plugin, wired into the backend |
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" localhost:7007/api/readiness/checks | Call your API as a script |
yarn tsc | Type-check everything |
A backend plugin
export const readinessPlugin = createBackendPlugin({
pluginId: 'readiness', // -> /api/readiness
register(env) {
env.registerInit({
deps: {
httpAuth: coreServices.httpAuth,
httpRouter: coreServices.httpRouter,
catalog: catalogServiceRef,
},
async init({ httpAuth, httpRouter, catalog }) {
httpRouter.use(await createRouter({ httpAuth, catalog, checks: defaultChecks }));
},
});
},
});
Test yourself
Answer in your head first, then open the answer.
Why does /evaluate pass the caller's credentials to the catalog?
So the catalog applies its own permissions to what the caller may see.
Where does httpRouter mount the readiness routes?
Under /api/readiness, from the plugin id.
A route throws new NotFoundError(...). What does the caller get?
A 404 with a JSON body that carries the message.
Where module 01 leaves you: The backend serves /api/readiness. It lists five checks and scores checkout at 80 (it has no runbook link). Merged in your portal repo.