Merge pull request #22164 from backstage/plugin-docs
docs/frontend-system: add plugin intro and extension type docs
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
---
|
||||
id: index
|
||||
title: Building Frontend Plugins
|
||||
sidebar_label: Overview
|
||||
# prettier-ignore
|
||||
description: Building frontend plugins using the new frontend system
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
This section covers how to build your own frontend [plugins](../architecture/04-plugins.md) and
|
||||
[overrides](../architecture/05-extension-overrides.md). They are sometimes collectively referred to as
|
||||
frontend _features_, and what you install to build up a Backstage frontend [app](../architecture/02-app.md).
|
||||
|
||||
## Creating a new plugin
|
||||
|
||||
This guide assumes that you already have a Backstage project set up. Even if you only want to develop a single plugin for publishing, we still recommend that you do so in a standard Backstage monorepo project, as you often end up needing multiple packages. For instructions on how to set up a new project, see our [getting started](../../getting-started/index.md#prerequisites) documentation.
|
||||
|
||||
To create a frontend plugin, run `yarn new`, select `plugin`, and fill out the rest of the prompts. This will create a new package at `plugins/<pluginId>`, which will be the main entrypoint for your plugin.
|
||||
|
||||
> **NOT: The created plugin will currently be templated for use in the legacy frontend system, and you will need to replace the existing plugin wiring code.**
|
||||
|
||||
## The plugin instance
|
||||
|
||||
The starting point of a frontend plugin is the `createPlugin` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-plugin-api`, which is where you will find most of the common APIs for building plugins.
|
||||
|
||||
This is how to create a minimal plugin:
|
||||
|
||||
```tsx title="in src/plugin.ts"
|
||||
import { createPlugin } from '@backstage/frontend-plugin-api';
|
||||
|
||||
export const examplePlugin = createPlugin({
|
||||
id: 'example',
|
||||
extensions: [],
|
||||
});
|
||||
```
|
||||
|
||||
```tsx title="in src/index.ts"
|
||||
export { examplePlugin as default } from './plugin';
|
||||
```
|
||||
|
||||
Note that we export the plugin as the default export of our package from `src/index.ts`. This is important, as it is how users of our plugin are able to seamlessly install the plugin package in a Backstage app without having to reference the plugin instance through code.
|
||||
|
||||
The plugin ID should be a lowercase dash-separated string, while the plugin instance variable should be the camel case version of the ID with a `Plugin` suffix. For more details on naming patterns within the frontend system, see [the article on naming patterns](../architecture/08-naming-patterns.md). By sticking to these naming patterns you ensure that users of your plugin more easily recognize the exports and features provided by your plugin.
|
||||
|
||||
## Adding extensions
|
||||
|
||||
The plugin that we created above is empty, and doesn't provide any actual functionality. To add functionality to a plugin you need to create and provide it with one or more [extensions](../architecture/03-extensions.md). Let's continue by adding a standalone page to our plugin, as well as a navigation item that allows users to navigate to the page.
|
||||
|
||||
To create a new extension you typically use pre-defined [extension creators](../architecture/03-extensions.md#extension-creators), provided either by the framework itself or by other plugins. In this case we'll need to use `createPageExtension` and `createNavItemExtension`, both from `@backstage/frontend-plugin-api`. We will also need to [create a route reference](../architecture/07-routes.md#creating-a-route-reference) to use as a reference for out page, allowing us to dynamically create URLs that link to our page.
|
||||
|
||||
```tsx title="in src/routes.ts"
|
||||
import { createRouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// Typically all routes are defined in src/routes.ts, in order to avoid circular imports.
|
||||
|
||||
// This will be the route reference for our example page. If you want to link
|
||||
// to the page from somewhere else, you can use this reference to generate the target path.
|
||||
export const rootRouteRef = createRouteRef();
|
||||
```
|
||||
|
||||
```tsx title="in src/plugin.ts"
|
||||
import {
|
||||
createPlugin,
|
||||
createPageExtension,
|
||||
createNavItemExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
// Note that these extensions aren't exported, only the plugin itself it.
|
||||
// You can export it locally for testing purposes, but don't export it from the plugin package.
|
||||
const examplePage = createPageExtension({
|
||||
routeRef: rootRouteRef,
|
||||
|
||||
// This is the default path of this page, but integrators are free to override it
|
||||
defaultPath: '/example',
|
||||
|
||||
// Page extensions are always dynamically loaded using React.lazy().
|
||||
// All of the functionality of this page is implemented in the
|
||||
// ExamplePage component, which is a regular React component.
|
||||
// highlight-next-line
|
||||
loader: () => import('./components/ExamplePage').then(m => <m.ExamplePage />),
|
||||
});
|
||||
|
||||
// This nav item is provided to the app.nav extension, and will by default be rendered as a sidebar item
|
||||
const exampleNavItem = createNavItemExtension({
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Example',
|
||||
icon: ExampleIcon, // Custom SvgIcon, or one from the Material UI icon library
|
||||
});
|
||||
|
||||
// The same plugin as above, now with the extensions added
|
||||
export const examplePlugin = createPlugin({
|
||||
id: 'example',
|
||||
extensions: [examplePage, exampleNavItem],
|
||||
// We can also make routes available to other plugins.
|
||||
// highlight-start
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
// highlight-end
|
||||
});
|
||||
```
|
||||
|
||||
What we've built here is a very common type of plugin. It's a top-level tool that provides a single page, along with a method for navigating to that page. The implementation of the page component, in this case the highlighted `ExamplePage`, can be arbitrarily complex. It can be anything from a single simple information page, to a full-blown application with multiple sub-pages.
|
||||
|
||||
We have also provided external access to our route reference by passing it to the plugin `routes` option. This makes it possible for app integrators to bind an external link from a different plugin to our plugin page. You can read more about how this works in the [External Route References](../architecture/07-routes.md#external-router-references) section.
|
||||
|
||||
## Utility APIs
|
||||
|
||||
Another type of extensions that is commonly used are [Utility APIs](../utility-apis/01-index.md). They can encapsulate shared pieces of functionality of your plugin, for example an API client for a backend service. You can optionally export your Utility API for other plugins to use, or allow integrators to replace the implementation of your Utility API with their own. For details on how to define and provide your own Utility API in your plugin, see the section on [creating Utility APIs](../utility-apis/02-creating.md).
|
||||
|
||||
What we'll show here is a complete example of a simple Utility API used only within the plugin itself:
|
||||
|
||||
```tsx title="src/api.ts - Defining an interface, API reference, and default implementation"
|
||||
import { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
export interface ExampleApi {
|
||||
getExample(): { example: string };
|
||||
}
|
||||
|
||||
export const exampleApiRef = createApiRef<ExampleApi>({
|
||||
id: 'plugin.example',
|
||||
});
|
||||
|
||||
export class DefaultExampleApi implements ExampleApi {
|
||||
getExample(): { example: string } {
|
||||
return { example: 'Hello World!' };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```tsx title="src/components/ExamplePage.tsx - Using the API in our page component"
|
||||
import { useApi } from '@backstage/frontend-plugin-api';
|
||||
import { exampleApiRef } from '../api';
|
||||
|
||||
export function ExamplePage() {
|
||||
// highlight-next-line
|
||||
const exampleApi = useApi(exampleApiRef);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Example Page</h1>
|
||||
<p>Example: {exampleApi.getExample()}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```tsx title="in src/plugin.ts - Registering a factory for our API"
|
||||
import {
|
||||
createApiFactory,
|
||||
createApiExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { exampleApiRef, DefaultExampleApi } from './api';
|
||||
|
||||
// highlight-add-start
|
||||
const exampleApi = createApiExtension({
|
||||
factory: createApiFactory({
|
||||
api: exampleApiRef,
|
||||
deps: {},
|
||||
factory: () => new DefaultExampleApi(),
|
||||
}),
|
||||
});
|
||||
// highlight-add-end
|
||||
|
||||
/* Omitted definitions for examplePage, exampleNavItem, and rootRouteRef. */
|
||||
|
||||
export const examplePlugin = createPlugin({
|
||||
id: 'example',
|
||||
extensions: [
|
||||
// highlight-add-next-line
|
||||
exampleApi,
|
||||
examplePage,
|
||||
exampleNavItem,
|
||||
],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Plugin specific extensions
|
||||
|
||||
There are many different plugins that you can extend with additional functionality through extensions. One such plugin is the catalog plugin, one of the core features of Backstage. It lets you catalog the software in your organization, where each item in the catalog has its own page that can be populated with tools and information relating to that catalog entity. In this example we will explore how our plugin can provide such a tool to display on an entity page.
|
||||
|
||||
```tsx title="in src/plugin.ts - An example entity content extension"
|
||||
import { createEntityContentExtension } from '@backstage/plugin-catalog-react';
|
||||
|
||||
// Entity content extension are similar to page extensions in that they are rendered at a route,
|
||||
// although they also have a title to support in-line navigation between the different content.
|
||||
// Just like a page extensions the content is lazy loaded, and you can also provide a
|
||||
// route reference if you want to be able to generate a URL that links to the content.
|
||||
const exampleEntityContent = createEntityContentExtension({
|
||||
defaultPath: 'example',
|
||||
defaultTitle: 'Example',
|
||||
loader: () =>
|
||||
import('./components/ExampleEntityContent').then(m => (
|
||||
<m.ExampleEntityContent />
|
||||
)),
|
||||
});
|
||||
```
|
||||
|
||||
The `ExampleEntityContent` itself is again a regular React component where you can implement any functionality you want. To access the entity that the content is being rendered for, you can use the `useEntity` hook from `@backstage/plugin-catalog-react`. You can see a full list of API provided by the catalog React library in [the API reference](../../reference/plugin-catalog-react.md).
|
||||
|
||||
For a more complete list of the different types of extensions that you can create for your plugin, see the [extension types](./03-extension-types.md) section.
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
id: extension-types
|
||||
title: Frontend System Extension Types
|
||||
sidebar_label: Extension Types
|
||||
# prettier-ignore
|
||||
description: Extension types provided by the frontend system and core features
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
This section covers many of the [extension types](../architecture/03-extensions.md#extension-creators) available at your disposal when building Backstage frontend plugins.
|
||||
|
||||
## Built-in extension types
|
||||
|
||||
These are the extension types provided by the Backstage frontend framework itself.
|
||||
|
||||
### Api - [Reference](../../reference/frontend-plugin-api.createapiextension.md)
|
||||
|
||||
An API extension is used to add or override [Utility API factories](../utility-apis/01-index.md) in the app. They are commonly used by plugins for both internal and shared APIs. There are also many built-in Api extensions provided by the framework that you are able to override.
|
||||
|
||||
### Component - [Reference](../../reference/frontend-plugin-api.createcomponentextension.md)
|
||||
|
||||
Components extensions are used to override the component associated with a component reference throughout the app.
|
||||
|
||||
### NavItem - [Reference](../../reference/frontend-plugin-api.createnavitemextension.md)
|
||||
|
||||
Navigation item extensions are used to provide menu items that link to different parts of the app. By default nav items are attached to the app nav extension, which by default is rendered as the left sidebar in the app.
|
||||
|
||||
### Page - [Reference](../../reference/frontend-plugin-api.createpageextension.md)
|
||||
|
||||
Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes.
|
||||
|
||||
### SignInPage - [Reference](../../reference/frontend-plugin-api.createsigninpageextension.md)
|
||||
|
||||
Sign-in page extension have a single purpose - to implement a custom sign-in page. They are always attached to the app root extension and are rendered before the rest of the app until the user is signed in.
|
||||
|
||||
### Theme - [Reference](../../reference/frontend-plugin-api.createthemeextension.md)
|
||||
|
||||
Theme extensions provide custom themes for the app. They are always attached to the app extension and you can have any number of themes extensions installed in an app at once, letting the user choose which theme to use.
|
||||
|
||||
### Translation - [Reference](../../reference/frontend-plugin-api.createtranslationextension.md)
|
||||
|
||||
Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages.
|
||||
|
||||
## Core feature extension types
|
||||
|
||||
These are the extension types provided by the Backstage core feature plugins.
|
||||
|
||||
### EntityCard - [Reference](https://github.com/backstage/backstage/blob/master/plugins/catalog-react/api-report-alpha.md)
|
||||
|
||||
Creates entity cards to be displayed on the entity pages of the catalog plugin.
|
||||
|
||||
### EntityContent - [Reference](https://github.com/backstage/backstage/blob/master/plugins/catalog-react/api-report-alpha.md)
|
||||
|
||||
Creates entity content to be displayed on the entity pages of the catalog plugin.
|
||||
|
||||
### SearchResultListItem - [Reference](https://github.com/backstage/backstage/blob/master/plugins/search-react/api-report-alpha.md)
|
||||
|
||||
Creates search result list items for different types of search results, to be displayed in search result lists.
|
||||
Reference in New Issue
Block a user