PlatformEngr® Backstage plugins course

Cheat notes · Module 06 · The frontend plugin

Talk to the backend: a utility 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

Components should not build URLs or attach tokens. A utility API hides that behind an interface: createApiRef names it, ApiBlueprint provides it, and discoveryApi plus fetchApi call your backend as the signed-in user.

Remember

  • API ref: createApiRef<ReadinessApi>({ id: 'plugin.readiness' }). Components ask with useApi(readinessApiRef).
  • ApiBlueprint: an extension that provides an implementation from other APIs (deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef }). It must be in the plugin's extensions.
  • discoveryApi.getBaseUrl('readiness'): the plugin's base URL from backend.baseUrl, so the same code works locally and in production.
  • fetchApi.fetch: fetch with the user's Backstage token added, so the backend sees the user and permissions apply.
  • Errors: throw await ResponseError.fromResponse(response); treat a 404 as a normal "not evaluated yet" state (undefined).
  • Testing: provide a fake ReadinessApi for the ref; components depend on the interface.

Pitfalls

  • Dropping the API extension from extensions in a refactor: TypeScript is happy, the page shows NotImplementedError: No implementation available for apiRef{plugin.readiness}. Check api:readiness is in the Visualizer.
  • Using the browser's fetch: no token, so every call is a 401.

Provide the client

export const readinessApi = ApiBlueprint.make({
  params: defineParams =>
    defineParams({
      api: readinessApiRef,
      deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
      factory: ({ discoveryApi, fetchApi }) =>
        new ReadinessClient(discoveryApi, fetchApi),
    }),
});

extensions: [readinessApi, readinessPage],

Test yourself

Answer in your head first, then open the answer.

Why does the client use fetchApi instead of fetch?

It adds the signed-in user's token, so the backend knows who calls.

How does the client know where the readiness backend is?

discoveryApi.getBaseUrl('readiness'), from the app's config.

How do you test the page without a backend?

Provide a fake ReadinessApi for readinessApiRef in the test.

Where module 06 leaves you: /readiness lists every scorecard, lowest score first, fetched with your sign-in token. Merged.