PlatformEngr® Backstage course

Cheat notes · Module 03 · The portal and its catalog

The catalog: teams, systems and a real service

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

The catalog is small YAML entities connected by refs. You write one side of a relation (owner, system, providesApis, dependsOn) and the catalog stores both sides, which is what makes "who owns this, what breaks if it goes down" answerable.

Remember

  • Entity: apiVersion, kind, metadata (name, title, annotations), spec. Kinds here: Group, User, Domain, System, Component, API, Resource.
  • Entity ref: kind:namespace/name, e.g. group:default/team-checkout. Fields that imply the kind take the short form: owner: team-checkout.
  • Relations: spec.owner on a component stores ownedBy on it and ownerOf on the group. Refs are not checked: one to a missing entity is stored anyway.
  • Location: where the catalog reads YAML: a file next to the backend (paths relative to packages/backend) or a URL in Git. Each location can limit kinds with rules: - allow: [...].
  • Annotations plugins read: github.com/project-slug links the repo; backstage.io/techdocs-ref points at the docs. $text: ./openapi.yaml is resolved relative to the file's location.
  • Removing a location deletes its entities (the example guests group vanished).

Pitfalls

  • A typo in the owner (team-chekout): no error anywhere. The team page no longer lists the service and the owner link leads to a missing page. Use an owner picker in templates and a CI check that every ref resolves.
  • Making a person the owner. People leave; make the owner a team.

Commands

curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" "localhost:7007/api/catalog/entities?filter=kind=group" | jq -r '.[].metadata.name'List entities of one kind
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" localhost:7007/api/catalog/entities/by-name/component/default/checkout | jq -r '.relations[] | .type + " " + .targetRef'The relations the catalog built
curl -s -H "Authorization: Bearer $PORTAL_API_TOKEN" -X POST -H 'Content-Type: application/json' localhost:7007/api/catalog/refresh -d '{"entityRef":"component:default/checkout"}'Refresh one entity now

A component that is connected

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: checkout
  annotations:
    github.com/project-slug: OWNER/checkout
    backstage.io/techdocs-ref: dir:.
spec:
  type: service
  lifecycle: production
  owner: team-checkout          # -> ownedBy / ownerOf
  system: shop                  # -> partOf
  providesApis: [checkout-api]  # -> providesApi
  dependsOn: [resource:checkout-db]

Test yourself

Answer in your head first, then open the answer.

You write spec.owner: team-checkout. What does the catalog store?

ownedBy on the component and ownerOf on the group.

A component names an owner that does not exist. What happens?

The ref is stored, the link leads nowhere, and nothing reports it.

Why does $text: ./openapi.yaml work for the checkout repo?

It is resolved relative to the URL of catalog-info.yaml and read with the GitHub token.

Where module 03 leaves you: The catalog holds Acme's teams, you as a User, two systems, and checkout with its API and database. The example data is gone. Your checkout repo exists on GitHub.