Documentation skeleton (#1617)
* Documentation skeleton * Added ADR list and more * rm material theme * Update mkdocs.yml * Add docs TOC * Work for plain MD and mkdocs * Update mkdocs.yml * Restructuring * Added roadmap * Add reference APIs * move publishing
This commit is contained in:
@@ -1,43 +0,0 @@
|
||||
# Create a Backstage Plugin
|
||||
|
||||
A Backstage Plugin adds functionality to Backstage.
|
||||
|
||||
## Create a Plugin
|
||||
|
||||
To create a new plugin, make sure you've run `yarn install` and installed
|
||||
dependencies, then run the following on your command line (invoking the
|
||||
`backstage-cli`).
|
||||
|
||||
```bash
|
||||
yarn create-plugin
|
||||
```
|
||||
|
||||
<p align='center'>
|
||||
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/create-plugin_output.png' width='600' alt='create plugin'>
|
||||
</p>
|
||||
|
||||
This will create a new Backstage Plugin based on the ID that was provided. It
|
||||
will be built and added to the Backstage App automatically.
|
||||
|
||||
_If `yarn start` is already running you should be able to see the default page
|
||||
for your new plugin directly by navigating to
|
||||
`http://localhost:3000/my-plugin`._
|
||||
|
||||
<p align='center'>
|
||||
<img src='https://github.com/spotify/backstage/raw/master/docs/getting-started/my-plugin_screenshot.png' width='600' alt='my plugin'>
|
||||
</p>
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin
|
||||
directory. Or by using the yarn workspace command, for example:
|
||||
|
||||
```bash
|
||||
yarn workspace @backstage/plugin-welcome start # Also supports --check
|
||||
```
|
||||
|
||||
This method of serving the plugin provides quicker iteration speed and a faster
|
||||
startup and hot reloads. It is only meant for local development, and the setup
|
||||
for it can be found inside the plugin's `dev/` directory.
|
||||
|
||||
[Next Step - Structure of a plugin](structure-of-a-plugin.md)
|
||||
|
||||
[Back to Getting Started](README.md)
|
||||
@@ -0,0 +1,79 @@
|
||||
# Backstage App
|
||||
|
||||
To get set up quickly with your own Backstage project you can create a Backstage
|
||||
App.
|
||||
|
||||
A Backstage App is a monorepo setup with `lerna` that includes everything you
|
||||
need to run Backstage in your own environment.
|
||||
|
||||
## Create an app
|
||||
|
||||
To create a Backstage app, you will need to have
|
||||
[NodeJS](https://nodejs.org/en/download/) Active LTS Release installed
|
||||
(currently v12).
|
||||
|
||||
With `npx`:
|
||||
|
||||
```bash
|
||||
npx @backstage/cli create-app
|
||||
```
|
||||
|
||||
This will create a new Backstage App inside the current folder. The name of the
|
||||
app-folder is the name that was provided when prompted.
|
||||
|
||||
<p align='center'>
|
||||
<img src='https://github.com/spotify/backstage/raw/master/docs/create-app_output.png' width='600' alt='create app'>
|
||||
</p>
|
||||
|
||||
Inside that directory, it will generate all the files and folder structure
|
||||
needed for you to run your app.
|
||||
|
||||
### Folder structure
|
||||
|
||||
```
|
||||
app
|
||||
├── README.md
|
||||
├── lerna.json
|
||||
├── package.json
|
||||
├── prettier.config.js
|
||||
├── tsconfig.json
|
||||
├── packages
|
||||
│ └── app
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── public
|
||||
│ │ └── ...
|
||||
│ └── src
|
||||
│ ├── App.test.tsx
|
||||
│ ├── App.tsx
|
||||
│ ├── index.tsx
|
||||
│ ├── plugins.ts
|
||||
│ └── setupTests.ts
|
||||
└── plugins
|
||||
└── welcome
|
||||
├── README.md
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── src
|
||||
├── index.ts
|
||||
├── plugin.test.ts
|
||||
├── plugin.ts
|
||||
├── setupTests.ts
|
||||
└── components
|
||||
├── Timer
|
||||
│ └── ...
|
||||
└── WelcomePage
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Run the app
|
||||
|
||||
When the installation is complete you can open the app folder and start the app.
|
||||
|
||||
```bash
|
||||
cd my-backstage-app
|
||||
yarn start
|
||||
```
|
||||
|
||||
_When `yarn start` is ready it should open up a browser window displaying your
|
||||
app, if not you can navigate to `http://localhost:3000`._
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 303 KiB |
@@ -65,6 +65,5 @@ instance of Backstage with your own plugins.
|
||||
- [Create a Backstage Plugin](create-a-plugin.md)
|
||||
- [Structure of a Plugin](structure-of-a-plugin.md)
|
||||
- [Utility APIs](utility-apis.md)
|
||||
- Using Backstage components (TODO)
|
||||
|
||||
[Back to Docs](../README.md)
|
||||
@@ -1,107 +0,0 @@
|
||||
# Structure of a Plugin
|
||||
|
||||
Nice, you have a new plugin! We'll soon see how we can develop it into doing
|
||||
great things. But first off, let's look at what we get out of the box.
|
||||
|
||||
## Folder structure
|
||||
|
||||
The new plugin should look something like:
|
||||
|
||||
```
|
||||
new-plugin/
|
||||
dist/
|
||||
node_modules/
|
||||
src/
|
||||
components/
|
||||
ExampleComponent/
|
||||
ExampleComponent.test.tsx
|
||||
ExampleComponent.tsx
|
||||
index.ts
|
||||
ExampleFetchComponent/
|
||||
ExampleFetchComponent.test.tsx
|
||||
ExampleFetchComponent.tsx
|
||||
index.ts
|
||||
index.ts
|
||||
plugin.test.ts
|
||||
plugin.ts
|
||||
jest.config.js
|
||||
jest.setup.ts
|
||||
package.json
|
||||
README.md
|
||||
tsconfig.json
|
||||
```
|
||||
|
||||
You might note a thing or two. Yes, a plugin looks like a mini project on it's
|
||||
own with a `package.json` and a `src` folder. And this is because we want
|
||||
plugins to be separate packages. This makes it possible to ship plugins on npm
|
||||
and it lets you work on a plugin in isolation, without loading all the other
|
||||
plugins in a potentially big Backstage app.
|
||||
|
||||
The `index.ts` files are there to let us import from the folder path and not
|
||||
specific files. It's a way to have control over the exports in one file per
|
||||
folder.
|
||||
|
||||
## Base files
|
||||
|
||||
In the root folder you have some configuration for typescript and jest, the test
|
||||
runner. You get a readme to populate with info about your plugin and a
|
||||
package.json to declare the plugin dependencies, metadata and scripts.
|
||||
|
||||
## The plugin file
|
||||
|
||||
In the `src` folder we get to the interesting bits. Check out the `plugin.ts`:
|
||||
|
||||
```jsx
|
||||
import { createPlugin, createRouteRef } from '@backstage/core';
|
||||
import ExampleComponent from './components/ExampleComponent';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '/new-plugin',
|
||||
title: 'New plugin',
|
||||
});
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'new-plugin',
|
||||
register({ router }) {
|
||||
router.addRoute(rootRouteRef, ExampleComponent);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This is where the plugin is created and where it hooks into the app by declaring
|
||||
what component should be shown on what url. See reference docs for
|
||||
[createPlugin](../reference/createPlugin.md) or
|
||||
[router](../reference/createPlugin-router.md).
|
||||
|
||||
## Components
|
||||
|
||||
The generated plugin includes two example components to showcase how we
|
||||
structure our plugins. There are usually one or multiple page components and
|
||||
next to them you can split up the UI in as many components as you feel like.
|
||||
|
||||
We have the `ExamplePage` to show an example Backstage page component. The
|
||||
`ExampleFetchComponent` show cases the common task of making an async request to
|
||||
a public API and plot the response data in a table using Material-UI components.
|
||||
|
||||
You may tweak these components, rename them and/or replace them completely.
|
||||
|
||||
## Connecting the plugin to the Backstage app
|
||||
|
||||
There are two things needed for a Backstage app to start making use of a plugin.
|
||||
|
||||
1. Add plugin as dependency in `app/package.json`
|
||||
2. `import` plugin in `app/src/plugins.ts`
|
||||
|
||||
Luckily these two steps happen automatically when you create a plugin with the
|
||||
Backstage CLI.
|
||||
|
||||
## Talking to the outside world
|
||||
|
||||
If your plugin needs to communicate with services outside the backstage
|
||||
environment you will probably face challenges like CORS policies and/or
|
||||
backend-side authorization. To smooth this process out you can use proxy -
|
||||
either the one you already have (like nginx/haproxy/etc) or the proxy-backend
|
||||
plugin that we provide for the backstage backend.
|
||||
[Read more](../../plugins/proxy-backend/README.md)
|
||||
|
||||
[Back to Getting Started](README.md)
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,177 +0,0 @@
|
||||
# Utility APIs
|
||||
|
||||
## Introduction
|
||||
|
||||
Backstage Plugins strive to be self-contained, with as much functionality as
|
||||
possible residing within the plugin itself and its backend APIs. There will
|
||||
however always be a need for plugins to communicate outside of its boundaries,
|
||||
both with other plugins and the app itself.
|
||||
|
||||
Backstage provides two primary methods for plugins to communication across their
|
||||
boundaries in client-side code. The first one being the `createPlugin` API and
|
||||
the registration hooks passed to the `register` method, and the second one being
|
||||
Utility APIs. While the `createPlugin` API is focused on the initialization
|
||||
plugins and the app, the Utility APIs provide ways for plugins to communicate
|
||||
during their entire life cycle.
|
||||
|
||||
## Usage
|
||||
|
||||
Each Utility API is tied to an `ApiRef` instance, which is a global singleton
|
||||
object without any additional state or functionality, its only purpose is to
|
||||
reference Utility APIs. `ApiRef`s are create using `createApiRef`, which is
|
||||
exported by `@backstage/core`. There are many predefined Utility APIs defined in
|
||||
`@backstage/core`, and they're all exported with a name of the pattern
|
||||
`*ApiRef`, for example `errorApiRef`.
|
||||
|
||||
To access one of the Utility APIs inside a React component, use the `useApi`
|
||||
hook exported by `@backstage/core`, or the `withApis` HOC if you prefer class
|
||||
components. For example, the `ErrorApi` can be accessed like this:
|
||||
|
||||
```tsx
|
||||
import React, { FC } from 'react';
|
||||
import { useApi, errorApiRef } from '@backstage/core';
|
||||
|
||||
export const MyComponent: FC<{}> = () => {
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
// Signal to the app that something went wrong, and display the error to the user.
|
||||
const handleError = error => {
|
||||
errorApi.post(error);
|
||||
};
|
||||
|
||||
// the rest of the component ...
|
||||
};
|
||||
```
|
||||
|
||||
Note that there is no explicit type given for `ErrorApi`. This is because the
|
||||
`errorApiRef` has the type embedded, and `useApi` is able to infer the type.
|
||||
|
||||
Also note that consuming Utility APIs is not limited to plugins, it can be done
|
||||
from any component inside Backstage, including the ones in `@backstage/core`.
|
||||
The only requirement is that they are beneath the `AppProvider` in the react
|
||||
tree.
|
||||
|
||||
## Registering Utility API Implementations
|
||||
|
||||
The Backstage App is responsible for providing implementations for all Utility
|
||||
APIs required by plugins. The example app in this repo registers its APIs inside
|
||||
[src/apis.ts](/packages/app/src/apis.ts). Here's an example of how to wire up
|
||||
the `ErrorApi` inside an app:
|
||||
|
||||
```ts
|
||||
import {
|
||||
ApiRegistry,
|
||||
createApp,
|
||||
alertApiRef,
|
||||
errorApiRef,
|
||||
AlertApiForwarder,
|
||||
ErrorApiForwarder,
|
||||
ErrorAlerter,
|
||||
} from '@backstage/core';
|
||||
|
||||
const builder = ApiRegistry.builder();
|
||||
|
||||
// The alert API is a self-contained implementation that shows alerts to the user.
|
||||
const alertApi = builder.add(alertApiRef, new AlertApiForwarder());
|
||||
|
||||
// The error API uses the alert API to send error notifications to the user.
|
||||
builder.add(errorApiRef, new ErrorAlerter(alertApi, new ErrorApiForwarder()));
|
||||
|
||||
const app = createApp({
|
||||
apis: apiBuilder.build(),
|
||||
// ... other config
|
||||
});
|
||||
```
|
||||
|
||||
The `ApiRegistry` is used to register all Utility APIs in the app and associate
|
||||
them with `ApiRef`s. It implements the `ApiHolder` interface, which enables it
|
||||
to provide an API implementation given an `ApiRef`.
|
||||
|
||||
Note that our `ErrorApi` implementation depends on another Utility API, the
|
||||
`AlertApi`. This is the method with which APIs can depend on other APIs, using
|
||||
manual dependency injection at the initialization of the app. In general, if you
|
||||
want to depend on another Utility API in an implementation of an API, you import
|
||||
the type for that API and make it a constructor parameter.
|
||||
|
||||
## Custom implementations of Utility APIs
|
||||
|
||||
Defining a custom implementation of a utility API is easy, you simply need to
|
||||
export a class that `implements` the target API, for example:
|
||||
|
||||
```ts
|
||||
export class IgnoringErrorApi implements ErrorApi {
|
||||
post(error: Error, context?: ErrorContext) {
|
||||
// ignore error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `IgnoringErrorApi` would then be imported in the app, and wired up like
|
||||
this:
|
||||
|
||||
```ts
|
||||
builder.add(errorApiRef, new IgnoringErrorApi());
|
||||
```
|
||||
|
||||
Note that the above line will cause an error if `IgnoreErrorApi` does not fully
|
||||
implement the `ErrorApi`, as it is checked by the type embedded in the
|
||||
`errorApiRef` at compile time.
|
||||
|
||||
## Defining custom Utility APIs
|
||||
|
||||
The pattern for plugins defining their own Utility APIs is not fully established
|
||||
yet. The current way is for the plugin to export its own `ApiRef` and type for
|
||||
the API, along with one or more implementations. It is then up to the app to
|
||||
import, and register those APIs. See for example the
|
||||
[lighthouse](/plugins/lighthouse/src/api.ts) or
|
||||
[graphiql](/plugins/graphiql/src/lib/api/types.ts) plugins for examples of this.
|
||||
|
||||
The goal is to make this process a bit smoother, but that requires work in other
|
||||
parts of Backstage, like configuration management. So it remains as a TODO. If
|
||||
you have more questions regarding this, or have an idea for an API that you want
|
||||
to share outside your plugin, hit us up in
|
||||
[GitHub issues](https://github.com/spotify/backstage/issues/new/choose) or the
|
||||
[Backstage Discord server](https://discord.gg/EBHEGzX).
|
||||
|
||||
## Architecture
|
||||
|
||||
The `ApiRef` instances mentioned above provide a point of indirection between
|
||||
consumers and producers of Utility APIs. It allows for plugins and components to
|
||||
depend on APIs in a type-safe way, without having a direct reference to a
|
||||
concrete implementation of the APIs. The Apps are also given a lot of
|
||||
flexibility in what implementations to provide. As long as they adhere to the
|
||||
contract established by an `ApiRef`, they are free to choose any implementation
|
||||
they want.
|
||||
|
||||
The figure below shows the relationship between
|
||||
<span style="color: #82b366">different Apps</span>, that provide
|
||||
<span style="color: #6c8ebf">different implementations</span> of the
|
||||
<span style="color: #9673a6">FooApi</span>.
|
||||
<span style="color: #d6b656">Components</span> within Plugins then access the
|
||||
<span style="color: #9673a6">FooApi</span> via the
|
||||
<span style="color: #b85450">fooApiRef</span>.
|
||||
|
||||
<div style="text-align:center">
|
||||
<img src="utility-apis-fig1.svg" alt="Figure showing the relationship between utility APIs, the apps that provide them, and the plugins that consume them">
|
||||
</div>
|
||||
|
||||
The current method for connecting Utility API providers and consumers is via the
|
||||
React tree using an `ApiProvider`, which is added to the `AppProvider` of the
|
||||
`App`. In the future there may potentially be more ways to do this, in ways that
|
||||
are not tied to react. A design goal of the Utility APIs was to not have them
|
||||
directly tied to React.
|
||||
|
||||
The indirection provided by Utility APIs also makes it straightforward to test
|
||||
components that depend on APIs, and to provide a standard common development
|
||||
environment for plugins. A proper test wrapper with mocked API implementations
|
||||
is not yet ready, but it will provided as a part of `@backstage/test-utils`. It
|
||||
will provide mocked variants of APIs, with additional methods for asserting a
|
||||
component's interaction with the API.
|
||||
|
||||
The common development environment for plugins is included in
|
||||
`@backstage/dev-utils`, where the exported `createDevApp` function creates an
|
||||
application with implementations for all core APIs already present. Contrary to
|
||||
the method for wiring up Utility API implementations in an app created with
|
||||
`createApp`, `createDevApp` uses automatic dependency injection. This is to make
|
||||
it possible to replace any API implementation, and having that be reflected in
|
||||
dependents of that API.
|
||||
Reference in New Issue
Block a user