From 8037c55ea90698690c6a400ae5def4aa75eb478f Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 30 Sep 2022 16:13:51 +0200 Subject: [PATCH] initial backend docs Signed-off-by: Johan Haals --- docs/api/backend.md | 128 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 1 deletion(-) diff --git a/docs/api/backend.md b/docs/api/backend.md index 0990343a01..fd3da4fbd8 100644 --- a/docs/api/backend.md +++ b/docs/api/backend.md @@ -4,4 +4,130 @@ title: Backend description: About Backend --- -## TODO +## Backend System + +**DISCLAMER: The new backend system is under active development and is not considered stable** + +### Overview + +The default backend provides several services out of the box which are available to all plugins but there might cases where you want to provide a completely new service in your installation. + +### Service Refs + +A serviceRef is a named reference to an interface which are later used to resolve the actual service implementation. Conceptually this is very similar to `ApiRef`s in the frontend. +Services is what provides common utilities that previously resided in the `PluginEnvironment` such as Config, Logging and Database. + +On startup the backend will make sure that the services are initialized before being passed to the plugin/module that depend on them. +ServiceRefs does contain a scope which is used to determine if the serviceFactory creating the service will create a new instance for each plugin/module or if it will be shared. `plugin` scoped services will be created once per plugin and `root` scoped services will be created once per backend instance. + +#### Defining a ServiceRef + +In its simplest form the serviceRef can be defined like this referencing the type of the actual implementation. + +```ts +import { + createServiceFactory, + pluginMetadataServiceRef, + loggerServiceRef, +} from '@backstage/backend-plugin-api'; +import { ExampleImpl } from './ExampleImpl'; + +export interface ExampleApi { + doSomething(): Promise; +} + +export const exampleServiceRef = createServiceRef({ + id: 'example', + scope: 'plugin', // can be 'root' or 'plugin' + + // The defaultFactory is optional to implement but it will be used if no other factory is provided to the backend. + // This is allows for the backend to provide a default implementation of the service without having to wire it beforehand. + defaultFactory: async service => + createServiceFactory({ + service, + deps: { + logger: loggerServiceRef, + plugin: pluginMetadataServiceRef, + }, + // Logger is available directly in the factory as it's a root scoped service and will be created once per backend instance. + async factory({ logger }) { + + // plugin is available as it's a plugin scoped service and will be created once per plugin. + return async ({ plugin }) => { + // This block will be executed once per plugin depending on this serviceRef + logger.info(`Creating example service for for plugin ${plugin.id}`); + return new ExampleImpl({logger}); + }; + }, + }), +}), +``` + +### Overriding services + +In this example replace the default log implementation with a custom one. + +```ts +import { + createServiceFactory, + loggerServiceRef, +} from '@backstage/backend-plugin-api'; +export const gcpLoggerFactory = createServiceFactory({ + service: loggerServiceRef, + deps: {}, + async factory({}) { + return async ({}) => { + // This custom implementation conform with the type of the loggerServiceRef + return new GoogleCloudLogger(); + }; + }, +}); + +// packages/backend/src/index.ts +const backend = createBackend({ + services: [ + // supplies additional/replacement services to the backend + gcpLoggerFactory, + ], +}) +``` + +#### API Overview +`createBackend` +`createBackendPlugin` +`createBackendModule` +`createServiceRef` +`createExtensionPoint` +### Writing Plugins + +### Writing modules + +Some facts about modules + +- A Module is able to extend a plugin with additional functionality using the `ExtensionPoint`s registered by the plugin. +- A module can only extend one plugin but can interact with multiple `ExtensionPoint`s registered by that plugin. +- A module is always initialized before the plugin it extends. + +A module depend on the extensionPoint exported by the plugins library package(eg `catalog-node`, `scaffolder-backend`) and does not directly declare a dependency on the plugin package itself. + + + +### Overwriting services + + +### Extension Points + +```ts +import { createExtensionPoint } from '@backstage/backend-plugin-api'; + +export interface ScaffolderActionsExtensionPoint { + addAction(action: ScaffolderAction): void; +} + +export const ScaffolderActionsExtensionPoint = + createExtensionPoint({ + id: 'scaffolder.actions', + }); +``` + +### Testing