The anatomy of a Backstage Plugin
Recently I had the pleasure of creating a Backstage plugin rendering GitHub Issues.
Here is a little overview of how it works and what I learned.
First thing first — the what! 🧐
*No disrespect, but I’m assuming that the dear reader knows what Backstage is. However, if not, here is a link to this fantastic project home page https://backstage.io/.
The plugin, based on the well-known GitHub slug annotation associated with the Entity, renders the list of Open issues on GitHub. It downloads data from GitHub Graphql API; hence it requires GitHub Authentication Provider.
The plugin is designed to work with four Entity kinds, and it behaves a bit differently depending on that kind:
- For Groups and Users, it renders issues from all repositories for which the Entity is the owner,
- For APIs and Components, it renders issues from only one repository assigned to the Entity.
Let’s get down to business — the how! 🤓
I think it’s safe to say that Backstage is all about the Developer's Experience and the process of creating a plugin is no exception.
GitHub Issues Plugin was scaffolded with standard Backstage CLI cmd.
From the Backstage documentation, we can learn that:
Each plugin is treated as a self-contained web app and can include almost any type of content.
This is important because thinking about each plugin as a “self-contained web app” gives developers freedom in choosing how to structure their code. The file organisation I’m about to describe worked well for me, but it’s not the only correct one.
From a broad perspective, the GitHub Issues plugin does two things, it fetches the data from the third-party API and then renders it. The separation of these concerns is reflected in the code structure.
In the src directory, there are three folders, and each has a dedicated function:
- api/ — this is where the communication with GitHub API is encapsulated; this folder contains an internal plugin API,
- components/ — unsurprisingly, this is where the rendering logic lives,
- hooks/ — this is the one to connect React to the outside world, fetching the data from the API and reading rendering Entity information from Backstage.
Keeping the API and React worlds separate works as a nice divide-and-conquer technique. When building and testing the API, we are only concerned with problems related to efficiently querying the data. When working on the rendering part, we can treat the API as a black box, being interested only in the contract it exposes. We can mock and test the component's behaviour, not the implementation details. Additionally, this ease of mocking the API comes in handy with setting up the local dev environment when running the plugin in separation.
How to know your issues 🤪
Again Backstage is amazing…
It exposes React hooks that can easily give any information about the Entity you need. With the help of the
useEntity, we can access the Entity metadata, which renders the plugin. The metadata contains information about the associated repository.
In the case of API or Component Entities, this is enough. Groups and Users are a bit more complicated because they can own many GitHub repositories. We use another hook -
useApi, to get the instance of
catalogApi and find all owned Entities with the mighty metadata.
Having the list of GitHub repositories, the next step is to check if they contain any open issues.
GitHub Graphql API is excellent for this task. To avoid the race condition of sending too many parallel requests for the Entities that own multiple repositories, we can take advantage of a GraphQL fragment and combine our query into a single request.
I decided to implement that optimisation from the beginning because, at the time of creating the plugin, my team owned more than 100 repositories. However, after enabling the plugin in production, I learned that some of them were archived and, although still available in our Backstage querying their data resulted in GraphQL errors. Luckily we can easily recover from this pickle — use the successfully returned data and, even better, take advantage of Backstage
errorApi to notify the user about the problem.
gitHubIssuesApi is not used anywhere outside of the GHI plugin, so all its types and the API itself are
internal. Something worth keeping in mind, as it was tempting to make it public, owning the fact the React hook imports it in another module. I was gently moved in the right direction during Pull Request.
Let’s make it pretty! 🤩
The last thing to do is to render the data to the user. This task is done with standard modern React. I invite the dear reader to have a pick around at my code if they wish. However, React development is not the main focus of this article. What I would like to mention is that Backstage also exposes a library of standard components that help a lot. Here is the Storybook.
They are based on Material UI and let us stay consistent with the project UI design standards and support themes. However, pure Material UI is supported if you need to build custom components and works like a charm with the rest of the Backstage FE ecosystem.
It’s not only about the code. It’s all about the people! 👯♀️
The Backstage team is excellent. They are easy to contact on Discord and are very helpful during the PR review.
The community around the project has fresh and inspiring energy; every month, there is a community session.
I had the pleasure of presenting the plugin at one of these sessions, and here is the presentation recording.
Represent and stay fly! Aiiiii!