Merge pull request #25903 from backstage/rugvip/presets
backend-*-api: add support for feature loaders
This commit is contained in:
@@ -193,7 +193,7 @@ backend.add(import('@backstage/plugin-auth-backend-module-github-provider'));
|
||||
backend.add(customAuth);
|
||||
```
|
||||
|
||||
Check out [the naming patterns article](../backend-system/architecture/07-naming-patterns.md) for what rules
|
||||
Check out [the naming patterns article](../backend-system/architecture/08-naming-patterns.md) for what rules
|
||||
apply regarding how to form valid IDs. In this example we also put the module
|
||||
declaration directly in `packages/backend/src/index.ts` but that's just for
|
||||
simplicity. You can place it anywhere you like, including in other packages, and
|
||||
|
||||
@@ -38,7 +38,7 @@ export const fooServiceRef = createServiceRef<FooService>({
|
||||
|
||||
The `fooServiceRef` that we create above should be exported, and can then be used to declare a dependency on the `FooService` interface and receive an implementation of it at runtime.
|
||||
|
||||
When creating a service reference you need to give it an ID. This ID needs to be globally unique, and should generally be of the format `'<pluginId>.<serviceName>'`. For more naming patterns surrounding services, see the [naming patterns](./07-naming-patterns.md#services) page.
|
||||
When creating a service reference you need to give it an ID. This ID needs to be globally unique, and should generally be of the format `'<pluginId>.<serviceName>'`. For more naming patterns surrounding services, see the [naming patterns](./08-naming-patterns.md#services) page.
|
||||
|
||||
A note on naming: the frontend and backend systems intentionally use the separate names "APIs" and "Services" for concepts that are quite similar. This is to avoid confusion between the two, both in documentation and discussion, but also in code. While the two systems are quite similar, they are not identical, and they can't be used interchangeably.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Plugins provide the actual base features of a Backstage backend. Each plugin ope
|
||||
|
||||
## Defining a Plugin
|
||||
|
||||
Plugins are created using the `createBackendPlugin` function, and should typically be exported from a plugin package. All plugins must have an ID and a `register` method, where the ID matches the plugin ID in the package name, without the `-backend` suffix. See also the [dedicated section](./07-naming-patterns.md) about proper naming patterns.
|
||||
Plugins are created using the `createBackendPlugin` function, and should typically be exported from a plugin package. All plugins must have an ID and a `register` method, where the ID matches the plugin ID in the package name, without the `-backend` suffix. See also the [dedicated section](./08-naming-patterns.md) about proper naming patterns.
|
||||
|
||||
```ts
|
||||
// plugins/example-backend/src/plugin.ts
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
id: feature-loaders
|
||||
title: Backend Feature Loaders
|
||||
sidebar_label: Feature Loaders
|
||||
# prettier-ignore
|
||||
description: Backend feature loaders
|
||||
---
|
||||
|
||||
Backend feature loaders are used to programmatically select and install features in a Backstage backend. They can service a wide range of use cases, such as enabling or disabling features based on static configuration, dynamically load features at runtime, or conditionally load features based on the state of a system.
|
||||
|
||||
Feature loaders are defined using the `createBackendFeatureLoader` function, exported by `@backstage/backend-plugin-api`. It accepts a `loader` function, as well as an optional `deps` object for declaring service dependencies. Unlike plugins and modules, feature loaders are limited to only depending on root-scoped services, but that still allows access to for example the [root config](../core-services/root-config.md) and [root logger](../core-services/root-logger.md) services.
|
||||
|
||||
The `loader` function can be defined in many different ways, with the main requirement being that it returns a list of `BackendFeature`s in some form. A backend feature is the kind of object that you can pass to `backend.add(...)`, for example services factories, plugins, modules, or even other feature loaders. The `loader` function can be synchronous or asynchronous, and can be defined as a generator function to allow for more complex logic.
|
||||
|
||||
## Examples
|
||||
|
||||
The following are a few example of how feature loaders can be used:
|
||||
|
||||
### Simple list of features
|
||||
|
||||
A feature loader can simply return a list of features to be installed:
|
||||
|
||||
```ts
|
||||
export default createBackendFeatureLoader({
|
||||
loader() {
|
||||
return [
|
||||
import('@backstage/plugin-search-backend/alpha'),
|
||||
import('@backstage/plugin-search-backend-module-catalog/alpha'),
|
||||
import('@backstage/plugin-search-backend-module-explore/alpha'),
|
||||
import('@backstage/plugin-search-backend-module-techdocs/alpha'),
|
||||
];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
It can also encapsulate a collection of custom features:
|
||||
|
||||
```ts
|
||||
export default createBackendFeatureLoader({
|
||||
// Async loader is fine too
|
||||
async loader() {
|
||||
return [
|
||||
createBackendPlugin({
|
||||
...
|
||||
}),
|
||||
createBackendModule({
|
||||
...
|
||||
}),
|
||||
]
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Conditional loading
|
||||
|
||||
A feature loader can access root-scoped services, such as the config service. This allows for conditional loading of features based on configuration. It is often convenient to use a generator function for this purpose:
|
||||
|
||||
```ts
|
||||
export default createBackendFeatureLoader({
|
||||
deps: {
|
||||
config: coreServices.rootConfig,
|
||||
},
|
||||
// The `*` in front of the function name makes it a generator function
|
||||
*loader({ config }) {
|
||||
// Example of a custom config flag to enable search
|
||||
if (config.getOptionalString('customFeatureToggle.search')) {
|
||||
yield import('@backstage/plugin-search-backend/alpha');
|
||||
yield import('@backstage/plugin-search-backend-module-catalog/alpha');
|
||||
yield import('@backstage/plugin-search-backend-module-explore/alpha');
|
||||
yield import('@backstage/plugin-search-backend-module-techdocs/alpha');
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Dynamic logic
|
||||
|
||||
A feature loader can also be asynchronous, and for example fetch data from an external source to determine which features to load:
|
||||
|
||||
```ts
|
||||
export default createBackendFeatureLoader({
|
||||
// The `async *` in front of the function name makes it an async generator function.
|
||||
async *loader() {
|
||||
const localMetadata = await readMetadataFromDisk();
|
||||
|
||||
if (localMetadata.enableSearch) {
|
||||
yield import('@backstage/plugin-search-backend/alpha');
|
||||
yield import('@backstage/plugin-search-backend-module-catalog/alpha');
|
||||
|
||||
const remoteMetadata = await fetchMetadata();
|
||||
|
||||
if (remoteMetadata.enableExplore) {
|
||||
yield import('@backstage/plugin-search-backend-module-explore/alpha');
|
||||
}
|
||||
if (remoteMetadata.enableTechDocs) {
|
||||
yield import('@backstage/plugin-search-backend-module-techdocs/alpha');
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -68,7 +68,7 @@ that's specific to your plugin. In the example above, the logger might tag
|
||||
messages with your plugin ID, and the HTTP router might prefix API routes with
|
||||
your plugin ID, depending on the implementation used.
|
||||
|
||||
See [the article on naming patterns](../architecture/07-naming-patterns.md) for
|
||||
See [the article on naming patterns](../architecture/08-naming-patterns.md) for
|
||||
details on how to best choose names/IDs for plugins and related backend system
|
||||
items.
|
||||
|
||||
@@ -124,7 +124,7 @@ export const catalogModuleExampleCustomProcessor = createBackendModule({
|
||||
export { catalogModuleExampleCustomProcessor as default } from './module';
|
||||
```
|
||||
|
||||
See [the article on naming patterns](../architecture/07-naming-patterns.md) for
|
||||
See [the article on naming patterns](../architecture/08-naming-patterns.md) for
|
||||
details on how to best choose names/IDs for modules and related backend system
|
||||
items.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user