Plugin Creation Tutorial

Backstage plugins and functionality extensions should be writen in TypeScript/Node.js because backstage is written in those languages

General Algorithm for Adding a Plugin in Backstage

  1. Create the Plugin
    To create a plugin in the project structure, you need to run the following command at the root of Backstage:

    yarn new --select plugin
    

    The wizard will ask you for the plugin ID, which will be its name. After that, a template for the plugin will be automatically created in the directory plugins/{plugin id}. After this install all needed dependencies. After this install required dependencies. In example case this is "axios" for API requests
    Emaple:

     yarn add axios
    
  2. Define the Plugin’s Functionality
    In the newly created plugin directory, focus on defining the plugin’s core functionality. This is where you will create components that handle the logic and user interface (UI) of the plugin. Place these components in the plugins/{plugin_id}/src/components/ folder, and if your plugin interacts with external data or APIs, manage those interactions within these components.

  3. Set Up Routes
    In the main configuration file of your plugin (typically plugins/{plugin_id}/src/routs.ts), set up the routes. Use createRouteRef() to define route references, and link them to the appropriate components in your plugins/{plugin_id}/src/components/ folder. Each route will determine which component renders for specific parts of the plugin.

  4. Register the Plugin
    Navigate to the packages/app folder and import your plugin into the main application. Register your plugin in the routs array within packages/app/src/App.tsx to integrate it into the Backstage system. It will create a rout for your’s plugin page

  5. Add Plugin to the Sidebar Menu
    To make the plugin accessible through the Backstage sidebar, modify the sidebar component in packages/app/src/components/Root.tsx. Add a new sidebar item linked to your plugin’s route reference, allowing users to easily access the plugin through the menu.

  6. Test the Plugin
    Run the Backstage development server using yarn dev and navigate to your plugin’s route via the sidebar or directly through its URL. Ensure that the plugin’s functionality works as expected.

Example

All steps will be demonstrated using a simple example plugin, which will request JSON files from the API of jsonplaceholder.typicode.com and display them on a page.

  1. Creating test-plugin:

    yarn new --select plugin
    

    Adding required dependencies. In this case only “axios” is needed for API requests

    yarn add axios
    
  2. Implement code of the plugin component in plugins/{plugin-id}/src/{Component name}/{filename}.tsx

    import React, { useState } from 'react';
    import axios from 'axios';
    import { Typography, Grid } from '@material-ui/core';
    import {
      InfoCard,
      Header,
      Page,
      Content,
      ContentHeader,
      SupportButton,
    } from '@backstage/core-components';
    
    export const TestComponent = () => {
      const [posts, setPosts] = useState<any[]>([]);
      const [loading, setLoading] = useState(false);
      const [error, setError] = useState<string | null>(null);
    
      const fetchPosts = async () => {
        setLoading(true);
        setError(null);
    
        try {
          const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
          setPosts(response.data);
        } catch (err) {
          setError('Ошибка при получении постов');
        } finally {
          setLoading(false);
        }
      };
    
      return (
        <Page themeId="tool">
          <Header title="Welcome to the Test Plugin!" subtitle="This is a subtitle">
            <SupportButton>A description of your plugin goes here.</SupportButton>
          </Header>
          <Content>
            <ContentHeader title="Posts Section">
              <SupportButton>
                Click to load posts from the API.
              </SupportButton>
            </ContentHeader>
            <Grid container spacing={3} direction="column">
              <Grid item>
                <InfoCard title="Information Card">
                  <Typography variant="body1">
                    This card contains information about the posts fetched from the API.
                  </Typography>
                  {loading && <Typography>Загрузка...</Typography>}
                  {error && <Typography color="error">{error}</Typography>}
                  {!loading && !posts.length && (
                    <button onClick={fetchPosts}>Request Posts</button>
                  )}
                </InfoCard>
              </Grid>
              <Grid item>
                {posts.length > 0 && (
                  <InfoCard title="Fetched Posts">
                    <ul>
                      {posts.map(post => (
                        <li key={post.id}>
                        <Typography variant="h6">{post.title}</Typography>
                        <Typography>{post.body}</Typography>
                        </li>
                       ))}
                    </ul>
                  </InfoCard>
                )}
              </Grid>
            </Grid>
          </Content>
        </Page>
      );
    };
    
  3. Setup routs in plugins/{plugin_id}/src/routs.ts

import { createRouteRef } from '@backstage/core-plugin-api';

export const rootRouteRef = createRouteRef({
  id: 'test-plugin',
});
  1. Register the plugin in packages/app/src/App.tsx in routes Import of the plugin:
import { TestPluginPage } from '@internal/backstage-plugin-test-plugin';

Adding route:

const routes = (
    <FlatRoutes>
       ... //{Other Routs}
       <Route path="/test-plugin" element={<TestPluginPage />} />
    </FlatRoutes>
)
  1. Add Item to sidebar menu of the backstage in packages/app/src/components/Root/Root.tsx. This should be added in to Root object as another SidebarItem
export const Root = ({ children }: PropsWithChildren<{}>) => (
  <SidebarPage>
    <Sidebar>
      ... //{Other sidebar items}
      <SidebarItem icon={ExtensionIcon} to="/test-plugin" text="Test Plugin" />
    </Sidebar>
    {children}
  </SidebarPage>
);
  1. Plugin is ready. Run the application
 yarn dev

example example