docs/api/utility-apis: links out to api reference
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
+106
-71
@@ -12,25 +12,32 @@ 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 communicate 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.
|
||||
boundaries in client-side code. The first one being the
|
||||
[createPlugin](../reference/core-plugin-api.createPlugin.md) API along with the
|
||||
extensions that it can provide, and the second one being Utility APIs. While the
|
||||
[createPlugin](../reference/core-plugin-api.createPlugin.md) API is focused on
|
||||
the initialization plugins and the app, the Utility APIs provide ways for
|
||||
plugins to communicate during their entire life cycle.
|
||||
|
||||
## Consuming APIs
|
||||
|
||||
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 created using `createApiRef`, which is
|
||||
exported by `@backstage/core-plugin-api`. There are many
|
||||
[predefined Utility APIs](../reference/utility-apis/README.md) defined in
|
||||
`@backstage/core-plugin-api`, and they're all exported with a name of the
|
||||
pattern `*ApiRef`, for example `errorApiRef`.
|
||||
Each Utility API is tied to an [ApiRef](../reference/core-plugin-api.ApiRef.md)
|
||||
instance, which is a global singleton object without any additional state or
|
||||
functionality, its only purpose is to reference Utility APIs.
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md)s are created using
|
||||
[createApiRef](../reference/core-plugin-api.createApiRef.md), which is exported
|
||||
by [@backstage/core-plugin-api](../reference/core-plugin-api.md). There are also
|
||||
many predefined Utility APIs in
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md), and they're all
|
||||
exported with a name of the pattern `*ApiRef`, for example
|
||||
[errorApiRef](../reference/core-plugin-api.errorApiRef.md).
|
||||
|
||||
To access one of the Utility APIs inside a React component, use the `useApi`
|
||||
hook exported by `@backstage/core-plugin-api`, or the `withApis` HOC if you
|
||||
prefer class components. For example, the `ErrorApi` can be accessed like this:
|
||||
To access one of the Utility APIs inside a React component, use the
|
||||
[useApi](../reference/core-plugin-api.useApi.md) hook exported by
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md), or the
|
||||
[withApis](../reference/core-plugin-api.withApis.md) HOC if you prefer class
|
||||
components. For example, the
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md) can be accessed like this:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
@@ -48,24 +55,31 @@ export const MyComponent = () => {
|
||||
};
|
||||
```
|
||||
|
||||
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.
|
||||
Note that there is no explicit type given for
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md). This is because the
|
||||
[errorApiRef](../reference/core-plugin-api.errorApiRef.md) has the type
|
||||
embedded, and [useApi](../reference/core-plugin-api.useApi.md) 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-plugin-api`. The only requirement is that they are beneath the
|
||||
`AppProvider` in the react tree.
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md). The only
|
||||
requirement is that they are beneath the `AppProvider` in the react tree.
|
||||
|
||||
## Supplying APIs
|
||||
|
||||
### API Factories
|
||||
|
||||
APIs are registered in the form of `ApiFactories`, which encapsulate the process
|
||||
of instantiating an API. It is a collection of three things: the `ApiRef` of the
|
||||
API to instantiate, a list of all required dependencies, and a factory function
|
||||
that returns a new API instance.
|
||||
APIs are registered in the form of
|
||||
[ApiFactories](../reference/core-plugin-api.ApiFactory.md), which encapsulate
|
||||
the process of instantiating an API. It is a collection of three things: the
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md) of the API to instantiate, a
|
||||
list of all required dependencies, and a factory function that returns a new API
|
||||
instance.
|
||||
|
||||
For example, this is the default `ApiFactory` for the `ErrorApi`:
|
||||
For example, this is the default
|
||||
[ApiFactory](../reference/core-plugin-api.ApiFactory.md) for the
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md):
|
||||
|
||||
```ts
|
||||
createApiFactory({
|
||||
@@ -79,18 +93,25 @@ createApiFactory({
|
||||
});
|
||||
```
|
||||
|
||||
In this example the `errorApiRef` is our API, which encapsulates the `ErrorApi`
|
||||
type. The `alertApiRef` is our single dependency, which we give the name
|
||||
`alertApi`, and is then passed on to the factory function, which returns an
|
||||
implementation of the `ErrorApi`.
|
||||
In this example the [errorApiRef](../reference/core-plugin-api.errorApiRef.md)
|
||||
is our API, which encapsulates the
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md) type. The
|
||||
[alertApiRef](../reference/core-plugin-api.alertApiRef.md) is our single
|
||||
dependency, which we give the name `alertApi`, and is then passed on to the
|
||||
factory function, which returns an implementation of the
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md).
|
||||
|
||||
The `createApiFactory` function is a thin wrapper that enables TypeScript type
|
||||
inference. You may notice that there are no type annotations in the above
|
||||
example, and that is because we're able to infer all types from the `ApiRef`s.
|
||||
TypeScript will make sure that the return value of the `factory` function
|
||||
matches the type embedded in `api`'s `ApiRef`, in this case the `ErrorApi`. It
|
||||
will also match the types between the `deps` and the parameters of the `factory`
|
||||
function, again using the type embedded within the `ApiRef`s.
|
||||
The [createApiFactory](../reference/core-plugin-api.createApiFactory.md)
|
||||
function is a thin wrapper that enables TypeScript type inference. You may
|
||||
notice that there are no type annotations in the above example, and that is
|
||||
because we're able to infer all types from the
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md)s. TypeScript will make sure
|
||||
that the return value of the `factory` function matches the type embedded in
|
||||
`api`'s [ApiRef](../reference/core-plugin-api.ApiRef.md), in this case the
|
||||
[ErrorApi](../reference/core-plugin-api.ErrorApi.md). It will also match the
|
||||
types between the `deps` and the parameters of the `factory` function, again
|
||||
using the type embedded within the
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md)s.
|
||||
|
||||
## Registering API Factories
|
||||
|
||||
@@ -102,24 +123,27 @@ app, and the app itself.
|
||||
|
||||
Starting with the Backstage core library, it provides implementations for all of
|
||||
the core APIs. The core APIs are the ones exported by
|
||||
`@backstage/core-plugin-api`, such as the `errorApiRef` and `configApiRef`. You
|
||||
can find a full list of them [here](../reference/utility-apis/README.md).
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md), such as the
|
||||
[errorApiRef](../reference/core-plugin-api.errorApiRef.md) and
|
||||
[configApiRef](../reference/core-plugin-api.configApiRef.md).
|
||||
|
||||
The core APIs are loaded for any app created with `createApp` from
|
||||
`@backstage/core-plugin-api`, which means that there is no step that needs to be
|
||||
taken to include these APIs in an app.
|
||||
The core APIs are loaded for any app created with
|
||||
[createApp](../reference/core-app-api.createApp.md) from
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md), which means that
|
||||
there is no step that needs to be taken to include these APIs in an app.
|
||||
|
||||
### Plugin APIs
|
||||
|
||||
In addition to the core APIs, plugins can define and export their own APIs.
|
||||
While doing so they should usually also provide default implementations of their
|
||||
own APIs, for example, the `catalog` plugin exports `catalogApiRef`, and also
|
||||
supplies a default `ApiFactory` of that API using the `CatalogClient`. There is
|
||||
one restriction to plugin-provided API Factories: plugins may not supply
|
||||
factories for core APIs, trying to do so will cause the app to refuse to start.
|
||||
supplies a default [ApiFactory](../reference/core-plugin-api.ApiFactory.md) of
|
||||
that API using the `CatalogClient`. There is one restriction to plugin-provided
|
||||
API Factories: plugins may not supply factories for core APIs, trying to do so
|
||||
will cause the app to refuse to start.
|
||||
|
||||
Plugins supply their APIs through the `apis` option of `createPlugin`, for
|
||||
example:
|
||||
Plugins supply their APIs through the `apis` option of
|
||||
[createPlugin](../reference/core-plugin-api.createPlugin.md), for example:
|
||||
|
||||
```ts
|
||||
export const techdocsPlugin = createPlugin({
|
||||
@@ -144,7 +168,8 @@ Lastly, the app itself is the final point where APIs can be added, and what has
|
||||
the final say in what APIs will be loaded at runtime. The app may override the
|
||||
factories for any of the core or plugin APIs, with the exception of the config,
|
||||
app theme, and identity APIs. These are static APIs that are tied into the
|
||||
`createApp` implementation, and therefore not possible to override.
|
||||
[createApp](../reference/core-app-api.createApp.md) implementation, and
|
||||
therefore not possible to override.
|
||||
|
||||
Overriding APIs is useful for apps that want to switch out behavior to tailor it
|
||||
to their environment. In some cases plugins may also export multiple
|
||||
@@ -206,16 +231,19 @@ const app = createApp({
|
||||
```
|
||||
|
||||
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.
|
||||
implement the [ErrorApi](../reference/core-plugin-api.ErrorApi.md), as it is
|
||||
checked by the type embedded in the
|
||||
[errorApiRef](../reference/core-plugin-api.errorApiRef.md) at compile time.
|
||||
|
||||
## Defining custom Utility APIs
|
||||
|
||||
Plugins are free to define their own Utility APIs. Simply define the TypeScript
|
||||
interface for the API, and create an `ApiRef` using `createApiRef` exported from
|
||||
`@backstage/core-plugin-api`. Also be sure to provide at least one
|
||||
implementation of the API, and to declare a default factory for the API in
|
||||
`createPlugin`.
|
||||
interface for the API, and create an
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md) using
|
||||
[createApiRef](../reference/core-plugin-api.createApiRef.md) exported from
|
||||
[@backstage/core-plugin-api](../reference/core-plugin-api.md). Also be sure to
|
||||
provide at least one implementation of the API, and to declare a default factory
|
||||
for the API in [createPlugin](../reference/core-plugin-api.createPlugin.md).
|
||||
|
||||
Custom Utility APIs can be either public or private, which is up to the plugin
|
||||
to choose. Private APIs do not expose an external API surface, and it's
|
||||
@@ -226,15 +254,18 @@ plugin to override the API in the app. It is however important to maintain
|
||||
backwards compatibility of public APIs, as you may otherwise break apps that are
|
||||
using your plugin.
|
||||
|
||||
To make an API public, simply export the `ApiRef` of the API, and any associated
|
||||
types. To make an API private, just avoid exporting the `ApiRef`, but still be
|
||||
sure to supply a default factory to `createPlugin`.
|
||||
To make an API public, simply export the
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md) of the API, and any associated
|
||||
types. To make an API private, just avoid exporting the
|
||||
[ApiRef](../reference/core-plugin-api.ApiRef.md), but still be sure to supply a
|
||||
default factory to [createPlugin](../reference/core-plugin-api.createPlugin.md).
|
||||
|
||||
Private APIs are useful for plugins that want to depend on other APIs outside of
|
||||
React components, but not have to expose an entire API surface to maintain. When
|
||||
using private APIs, it is fine to use the `typeof` of an implementing class as
|
||||
the type parameter passed to `createApiRef`, while public APIs should always
|
||||
define a separate TypeScript interface type.
|
||||
the type parameter passed to
|
||||
[createApiRef](../reference/core-plugin-api.createApiRef.md), while public APIs
|
||||
should always define a separate TypeScript interface type.
|
||||
|
||||
Plugins may depend on APIs from other plugins, both in React components and as
|
||||
dependencies to API factories. Do however be sure to not cause circular
|
||||
@@ -242,13 +273,14 @@ dependencies between plugins.
|
||||
|
||||
## 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 [ApiRef](../reference/core-plugin-api.ApiRef.md) 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](../reference/core-plugin-api.ApiRef.md), 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
|
||||
@@ -271,14 +303,17 @@ 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 be 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.
|
||||
is not yet ready, but it will be provided as a part of
|
||||
[@backstage/test-utils](../reference/test-utils.md). 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
|
||||
[@backstage/dev-utils](../reference/dev-utils.md), where the exported
|
||||
[createDevApp](../reference/dev-utils.createDevApp.md) 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.
|
||||
[createApp](../reference/core-app-api.createApp.md),
|
||||
[createDevApp](../reference/dev-utils.createDevApp.md) 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