From dce8cecce637a0593b98eba6b58d3f840280066e Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 23 Jan 2024 16:24:39 +0100 Subject: [PATCH 1/4] docs: add building apps index page Signed-off-by: Camila Belo --- .../frontend-system/building-apps/01-index.md | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 docs/frontend-system/building-apps/01-index.md diff --git a/docs/frontend-system/building-apps/01-index.md b/docs/frontend-system/building-apps/01-index.md new file mode 100644 index 0000000000..cf860c01d9 --- /dev/null +++ b/docs/frontend-system/building-apps/01-index.md @@ -0,0 +1,124 @@ +--- +id: index +title: Building Frontend Apps +sidebar_label: Overview +# prettier-ignore +description: Building frontend apps using the new frontend system +--- + +> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.** + +To get set up quickly with your own Backstage project you can create a Backstage App. + +A Backstage App is a monorepo setup that includes everything you need to run Backstage in your own environment. + +## Creating a new app + +To create a Backstage app, you will need to have Node.js Active LTS Release installed. + +The easiest way to run the create app package is with `npx`: + +```sh +npx @backstage/create-app@latest +``` + +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. + +:::note +The created app will currently be templated for use in the legacy frontend system, and you will need to replace the existing app wiring code. +::: + +## The app instance + +The starting point of a frontend app is the `createApp` function, which accepts a single options object as its only parameter. It is imported from `@backstage/frontend-app-api`, which is where you will find most of the common APIs for building apps. + +This is how to create a minimal app: + +```tsx title="in src/index.ts" +import ReactDOM from 'react-dom/client'; +import { createApp } from '@backstage/frontend-app-api'; + +// Create your app instance +const app = createApp({ + // Features such as plugins can be installed explicitly, but we will explore other options later on + features: [catalogPlugin], +}); + +// This creates a React element that renders the entire app +const root = app.createRoot(); + +// Just like any other React we need a root element. No server side rendering is used. +const rootEl = document.getElementById('root')!; +``` + +Note that the `createRoot` exports an element instead of a component and it no longer accepts a custom root element as parameter, so this is the reason you can pass the returned element directly to React `createRoot`. See [customizing your root app element](#app-root) for more details on how to do it with extensions. + +## Configure your app + +### Enable features discovering + +This is how you enable the experimental feature discovering when building your app with the `@backstage/cli`, check out [here](https://backstage.io/docs/frontend-system/architecture/app#feature-discovery) for more details. + +:::warning +Remember that package extensions that are not auto-discovered must be manually added to the application when creating an app. See [features](#install-features-manually) for more details. +::: + +### Bind external routes + +This is the configuration you do when you want to associate an external plugin route ref with a regular one. There are two ways of doing it: via configuration file or through code. Access [this](https://backstage.io/docs/frontend-system/architecture/routes#binding-external-route-references) page for learning both ways. + +## Customize individual extensions + +It possible to enable, disable, order and configure extensions individually in the `app-config.yaml` config file. To get familiar with what is available for app extensions, go to the [built-in extensions](./02-built-in-extensions.md) documentation. For plugins customizations, we recommend you checking their read files instructions. + +## Install features manually + +A manual installation is required if your packages are not discovered automatically, either because you are not using `@backstage/cli` to build your application or because the features are defined in local modules in the app package. For manually install a feature you have to import your it and pass it to the `createApp` function: + +```tsx title="packages/app/src/App.tsx" +import { createApp } from '@backstage/frontend-app-api'; +// This plugin was create as a local module in the app +import { somePlugin } from './plugins'; + +const app = createApp({ + features: [somePlugin], +}); + +export default app.createRoot(); +``` + +:::info +You can also pass overrides to the features array, for more details, please read the [extension overrides](../architecture/05-extension-overrides.md) documentation. +::: + +If for some reason you need to perform asynchronous operations before passing features to the `createApp` function, you can use an async function as `features` option and this should return a features array promise: + +```tsx title="packages/app/src/App.tsx" +import { createApp } from '@backstage/frontend-app-api'; + +const app = createApp({ + // Lazy loading the plugin feature + features: import('./plugins/MyPlugin').then(m => [m.default]), +}); + +export default app.createRoot(); +``` + +## Lazy load your configuration file + +In some cases we want to load our configuration from a backend server or we just want to override the default app configuration loader. If you want to do it, you can pass an async callback to the `createApp` function and it should return a promise of the config object: + +```tsx title="packages/app/src/App.tsx" +import { createApp } from '@backstage/frontend-app-api'; + +const app = createApp({ + // Lazy loading the plugin feature + features: import('./plugins/MyPlugin').then(m => [m.default]), +}); + +export default app.createRoot(); +``` + +## Customize or override built-in extensions + +Previously you would customize the application route, components, apis, sidebar, etc. through the code in `App.tsx`. Now we want you to write less code and install more extensions to customize your Backstage instance. See [here](../building-plugins/03-extension-types.md) which types of extensions are available for you to customize your application. From 4c9072f1e1526427e90fa3875082e2eda644a178 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 23 Jan 2024 16:25:05 +0100 Subject: [PATCH 2/4] docs: add built in extensions page Signed-off-by: Camila Belo --- .../building-apps/02-built-in-extensions.md | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 docs/frontend-system/building-apps/02-built-in-extensions.md diff --git a/docs/frontend-system/building-apps/02-built-in-extensions.md b/docs/frontend-system/building-apps/02-built-in-extensions.md new file mode 100644 index 0000000000..be89e23c9e --- /dev/null +++ b/docs/frontend-system/building-apps/02-built-in-extensions.md @@ -0,0 +1,190 @@ +--- +id: built-in-extensions +title: App Built-in Extensions +sidebar_label: Built-in extensions +# prettier-ignore +description: Configuring or overriding built-in extensions +--- + +Built-in extensions are default app extensions that are always installed when you create a Backstage app. + +## Disable built-in extensions + +All built-in extensions can be disabled in the same way as you disable any other extension: + +```yaml title="app-config.yaml" +extensions: + # Disabling the built-in app root alert element + - app-root-element:app/alert-display: false +``` + +:::warning +Be careful when disabling built-in extensions, as there may be other extensions depending on their existence. For example, the built-in "alert display" extension displays messages retrieved via [AlertApi](https://backstage.io/docs/reference/core-plugin-api.alertapi) and disabling this extension will cause the application to no longer display these messages unless you install another extension that displays messages from `AlertApi`. +::: + +## Override built-in extensions + +You can override any built-in extension whenever their customizations, whether through configuration or input, do not meet a use case for your Backstage instance. Check out [this](../architecture/05-extension-overrides.md) documentation on how to override application extensions. + +:::warning +Be aware there could be some implementation requirements to properly override an built-in extension, such as using same apis and do not remove inputs or configurations otherwise you can cause a side effect in other parts of the system that expects same minimal behavior. +::: + +## Default built-in extensions + +### App + +This extension is the first extension attached to the extension tree. It is responsible for receiving the application's root element and other Frontend framework inputs. + +#### Inputs + +| Name | Description | Type | Optional | Default | Extension creator | +| ------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| root | The app root element. | [coreExtensionData.reactElement](https://backstage.io/docs/reference/frontend-plugin-api.coreextensiondata) | false | The [`App/Root`](#app-root) extension output. | No creator available, configure or override the [`App/Root`](#app-root) extension. | +| apis | The app apis factories. | [createApiExtension.factoryDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension.factorydataref) | false | See [default apis](#default-apis-extensions). | [createApiExtension](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension) | +| themes | The app themes list. | [createThemeExtension.themeDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension.themedataref) | false | See [default themes](#default-theme-extensions). | [createThemeExtension](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension) | +| components | The app components list. | [createComponentExtension.componentDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension.componentdataref) | - | false | See [default components](#defaults-components-extensions). | [createComponentExtension](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension) | +| translations | The app translations list. | [createTranslationExtension.translationDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension.translationdataref) | - | false | - | [createTranslationExtension](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension) | + +#### Default theme extensions + +Extensions that provides default theme inputs for the `App` extension. + +| kind | namespace | name | id | +| :---: | :-------: | :---: | :---------------: | +| theme | app | light | `theme:app/light` | +| theme | app | dark | `theme:app/dark` | + +#### Default components extensions + +Extensions that provides default components inputs for the `App` extension. + +| kind | namespace | name | id | +| :--------: | :-------: | :-----------------------------------: | :--------------------: | +| components | app | core.components.progress | `components:app/light` | +| components | app | core.components.notFoundErrorPage | `components:app/dark` | +| components | app | core.components.errorBoundaryFallback | `components:app/dark` | + +#### Default apis extensions + +Extensions that provides default apis inputs for the `App` extension. + +| kind | namespace | name | id | +| :--: | :------------------------: | :--: | :------------------------------: | +| api | core.discovery | - | `api:core.discovery` | +| api | core.alert | - | `api:core.alert` | +| api | core.analytics | - | `api:core.analytics` | +| api | core.error | - | `api:core.error` | +| api | core.storage | - | `api:core.storage` | +| api | core.fetch | - | `api:core.fetch` | +| api | core.oauthrequest | - | `api:core.oauthrequest` | +| api | core.auth.google | - | `api:core.auth.google` | +| api | core.auth.microsoft | - | `api:core.auth.microsoft` | +| api | core.auth.github | - | `api:core.auth.github` | +| api | core.auth.okta | - | `api:core.auth.okta` | +| api | core.auth.gitlab | - | `api:core.auth.gitlab` | +| api | core.auth.onelogin | - | `api:core.auth.onelogin` | +| api | core.auth.bitbucket | - | `api:core.auth.bitbucket` | +| api | core.auth.bitbucket-server | - | `api:core.auth.bitbucket-server` | +| api | core.auth.atlassian | - | `api:core.auth.atlassian` | +| api | plugin.permission.api | - | `api:plugin.permission.api` | + +### App root + +This is the extension that creates the app root element, so it renders root level components such as app router, layout. + +#### Inputs + +| Name | Description | Requirements | Optional | Default | Extension creator | +| ---------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| router | A React component that should manager the app routes context. | It must be one [router](https://reactrouter.com/en/main/routers/picking-a-router#web-projects) component or a custom component compatible with the 'react-router' library. | true | [BrowserRouter](https://reactrouter.com/en/main/router-components/browser-router) | [createRouterExtension](https://backstage.io/docs/reference/frontend-plugin-api.createrouterextension) | +| signInPage | A React component that should render the app sign-in page. | Should call the `onSignInSuccess` prop when the user has been successfully authorized, otherwise the user will not be correctly redirected to the application home page. | true | The default `AppRoot` extension does not use a default component for this input, it bypasses the user authentication check and always renders all routes when a login page is not installed. | [createSignInPageExtension](https://backstage.io/docs/reference/frontend-plugin-api.createsigninpageextension/) | +| children | A React component that renders the app sidebar and main content in a particular layout. | - | false | The [`App/Layout`](#app-layout) extension output. | No creator available, configure or override the [`App/Layout`](#app-layout) extension. | +| elements | React elements to be rendered outside of the app layout, such as shared popups. | - | false | See [default elements](#default-app-root-elements-extensions). | [createAppRootElementExtension](https://backstage.io/docs/reference/frontend-plugin-api.createapprootelementextension/) | +| wrappers | React components that should wrap the root element. | - | true | - | [createAppRootWrapperExtension](https://backstage.io/docs/reference/frontend-plugin-api.createapprootwrapperextension/) | + +#### Default app root elements extensions + +##### Alert Display + +An app root element extension that displays messages posted via the [`AlertApi`](https://backstage.io/docs/reference/core-plugin-api.alertapi). + +| kind | namespace | name | id | +| :--------------: | :-------: | :-----------: | :----------------------------------: | +| app-root-element | app | alert-display | `app-root-element:app/alert-display` | + +###### Configurations + +| Key | Type | Default value | Description | +| -------------------- | -------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- | +| `transientTimeoutMs` | number | 5000 | Time in milliseconds to wait before displaying messages | +| `anchorOrigin` | { vertical: 'top' \| 'bottom', horizontal: 'left' \| 'center' \| 'right' } | { vertical: 'top', horizontal: 'center' } | Position on the screen where the message alert will be displayed | + +###### Override or disable the extension + +If you do not want to display alerts, disable this extension or if the available settings do not meet your needs, override this extension. + +:::warning +The built-in "alert display" extension displays messages retrieved via [AlertApi](https://backstage.io/docs/reference/core-plugin-api.alertapi) and disabling this extension will cause the application to no longer display these messages unless you install another extension that displays messages from `AlertApi`. +::: + +##### OAuth Request Dialog + +An app root element extension that renders the oauth request dialog, it is based on the [oauthRequestApi](https://backstage.io/docs/reference/core-plugin-api.oauthrequestapi/). + +| kind | namespace | name | id | +| :--------------: | :-------: | :------------------: | :-----------------------------------------: | +| app-root-element | app | oauth-request-dialog | `app-root-element:app/oauth-request-dialog` | + +### App layout + +Renders the app's sidebar and content in a specific layout. + +| kind | namespace | name | id | +| :--: | :-------: | :----: | :----------: | +| - | app | layout | `app/layout` | + +#### Inputs + +| Name | Description | Type | Optional | Default | Extension creator | +| ------- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------- | ------- | ------------------------------------ | +| nav | A React element that renders the app sidebar. | [coreExtensionData.reactElement](https://backstage.io/docs/reference/frontend-plugin-api.coreextensiondata) | false | - | Override the `App/Nav` extension. | +| content | A React element that renders the app content. | [coreExtensionData.reactElement](https://backstage.io/docs/reference/frontend-plugin-api.coreextensiondata) | false | - | Override the `App/Routes` extension. | + +### App nav + +Extension responsible for rendering the logo and items in the app's sidebar. + +| kind | namespace | name | id | +| :--: | :-------: | :--: | :-------: | +| - | app | nav | `app/nav` | + +#### Inputs + +| Name | Description | Type | Optional | Default | Extension creator | +| ----- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- | -------------------------------------------------------------------------------------------------------- | +| logos | A nav logos object. | [createNavLogoExtension.logoElementsDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createnavlogoextension.logoelementsdataref) | true | - | [createNavLogoExtension](https://backstage.io/docs/reference/frontend-plugin-api.createnavlogoextension) | +| items | Nav items target objects. | [createNavItemExtension.targetDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension.targetdataref) | true | - | [createNavItemExtension](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension) | + +### App routes + +Renders a route element for each route received as input and a `NotFoundErrorPage` component. + +| kind | namespace | name | id | +| :--: | :-------: | :----: | :----------: | +| - | app | routes | `app/routes` | + +#### Caveats + +Be careful when overriding this extension, as to do so correctly you must consider these implementation requirements: + +- The routing system is managed by more than one extension, and they all use 'react-router' behind the scenes and there are also some utilities that are based on the same library like `useRouteRefParams`. Therefore, you cannot use a different library without causing side effects in these other extensions and helper utilities; +- Don't remove configs or inputs, just extend these things yourself with optional new options, otherwise it will cause breaking changes for extensions like `createPageExtension` that depend on this type of entry, for example; +- Remember to user the route refs for getting paths dynamically, otherwise if an adopter modifies a path through configuration, the route is not going to point to the configured path. +- Adopter expects to be able to customize the `NotFoundErrorPage` component via Components API, you should render this component for routes not configured. + +#### Inputs + +| Name | Description | Type | Optional | Default | Extension creator | +| ------ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | -------------------------------------------------------------------------------------------------- | +| routes | The route objects list. | `{ path: coreExtensionData.routePath, ref: coreExtensionData.routeRef.optional(), element: coreExtensionData.reactElement }` | false | - | [createPageExtension](https://backstage.io/docs/reference/frontend-plugin-api.createpageextension) | From 0796eb66da2bc1d2dae53b05aef69b68c83a6f04 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 23 Jan 2024 16:25:33 +0100 Subject: [PATCH 3/4] docs: update sidebar pages Signed-off-by: Camila Belo --- microsite/sidebars.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/microsite/sidebars.json b/microsite/sidebars.json index e76faac0ed..d52c00b3da 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -416,6 +416,14 @@ "frontend-system/building-plugins/extension-types" ] }, + { + "type": "category", + "label": "Building Apps", + "items": [ + "frontend-system/building-apps/index", + "frontend-system/building-apps/built-in-extensions" + ] + }, { "type": "category", "label": "Utility APIs", From f972f8d9f97af8d874958114d4f542c19018b7f3 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Sun, 4 Feb 2024 14:13:14 +0100 Subject: [PATCH 4/4] refactor: apply more review suggestions Signed-off-by: Camila Belo --- .../frontend-system/building-apps/01-index.md | 78 ++++++++++++------- .../building-apps/02-built-in-extensions.md | 42 +++++----- 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/docs/frontend-system/building-apps/01-index.md b/docs/frontend-system/building-apps/01-index.md index cf860c01d9..e23f628fb6 100644 --- a/docs/frontend-system/building-apps/01-index.md +++ b/docs/frontend-system/building-apps/01-index.md @@ -14,19 +14,19 @@ A Backstage App is a monorepo setup that includes everything you need to run Bac ## Creating a new app -To create a Backstage app, you will need to have Node.js Active LTS Release installed. +To create a new Backstage app we recommend using the `@backstage/create-app` command line, and the easiest way to run this package is with `npx`: -The easiest way to run the create app package is with `npx`: +:::note +The create-app CLI requires Node.js Active LTS Release. +::: ```sh +# The command bellow creates a Backstage App inside the current folder. +# The name of the app-folder is the name that was provided when prompted. npx @backstage/create-app@latest ``` -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. - -:::note -The created app will currently be templated for use in the legacy frontend system, and you will need to replace the existing app wiring code. -::: +The created-app is currently templated for legacy frontend system applications, so the app wiring code it creates needs to be migrated, see [the app instance](#the-app-instance) section for an example. ## The app instance @@ -37,6 +37,7 @@ This is how to create a minimal app: ```tsx title="in src/index.ts" import ReactDOM from 'react-dom/client'; import { createApp } from '@backstage/frontend-app-api'; +import catalogPlugin from '@backstage/plugin-catalog/alpha'; // Create your app instance const app = createApp({ @@ -49,31 +50,41 @@ const root = app.createRoot(); // Just like any other React we need a root element. No server side rendering is used. const rootEl = document.getElementById('root')!; + +ReactDOM.createRoot(rootEl).render(root); ``` -Note that the `createRoot` exports an element instead of a component and it no longer accepts a custom root element as parameter, so this is the reason you can pass the returned element directly to React `createRoot`. See [customizing your root app element](#app-root) for more details on how to do it with extensions. +Note that `createRoot` returns the root element that is rendered by React. The above example is installing a catalog plugin and using default settings for the app, as no options other than the `features` array are passed to the `createApp` function. + +Visit the [built-in extensions](#customize-or-override-built-in-extensions) section to see what is installed by default in a Backstage application. ## Configure your app -### Enable features discovering +### Bind external routes -This is how you enable the experimental feature discovering when building your app with the `@backstage/cli`, check out [here](https://backstage.io/docs/frontend-system/architecture/app#feature-discovery) for more details. +Linking routes from different plugins requires this configuration. You can do this either through a configuration file or by coding, visit [this](https://backstage.io/docs/frontend-system/architecture/routes#binding-external-route-references) page for instructions. + +### Enable feature discovery + +Use this setting to enable experimental feature discovery when building your app with `@backstage/cli`. With this configuration your application tries to discover and install package extensions automatically, check [here](../architecture/02-app.md#feature-discovery) for more details. :::warning Remember that package extensions that are not auto-discovered must be manually added to the application when creating an app. See [features](#install-features-manually) for more details. ::: -### Bind external routes +### Configure extensions individually -This is the configuration you do when you want to associate an external plugin route ref with a regular one. There are two ways of doing it: via configuration file or through code. Access [this](https://backstage.io/docs/frontend-system/architecture/routes#binding-external-route-references) page for learning both ways. +It is possible to enable, disable and configure extensions individually in the `app-config.yaml` config file. To get familiar with what is available for app extensions personalization, go to the [built-in extensions](./02-built-in-extensions.md) documentation. For plugin customizations, we recommend that you read the instructions in each plugin's README file. -## Customize individual extensions +### Customize or override built-in extensions -It possible to enable, disable, order and configure extensions individually in the `app-config.yaml` config file. To get familiar with what is available for app extensions, go to the [built-in extensions](./02-built-in-extensions.md) documentation. For plugins customizations, we recommend you checking their read files instructions. +Previously you would customize the application route, components, apis, sidebar, etc. through the code in `App.tsx`. Now we want you to write less code and install more extensions to customize your Backstage instance. See [here](../building-plugins/03-extension-types.md) which types of extensions are available for you to customize your application. -## Install features manually +## Use code to customize the app at a more granular level -A manual installation is required if your packages are not discovered automatically, either because you are not using `@backstage/cli` to build your application or because the features are defined in local modules in the app package. For manually install a feature you have to import your it and pass it to the `createApp` function: +### Install features manually + +A manual installation is required if your packages are not discovered automatically, either because you are not using `@backstage/cli` to build your application or because the features are defined in local modules in the app package. In order to manually install a feature, you must import it and pass it to the `createApp` function: ```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; @@ -91,34 +102,47 @@ export default app.createRoot(); You can also pass overrides to the features array, for more details, please read the [extension overrides](../architecture/05-extension-overrides.md) documentation. ::: -If for some reason you need to perform asynchronous operations before passing features to the `createApp` function, you can use an async function as `features` option and this should return a features array promise: +### Using an async features loader + +In case you need to perform asynchronous operations before passing features to the `createApp` function, define a [feature loader](https://backstage.io/docs/reference/frontend-app-api.createappfeatureloader/) object and pass it to the `features` option: ```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; const app = createApp({ - // Lazy loading the plugin feature - features: import('./plugins/MyPlugin').then(m => [m.default]), + features: { + getLoaderName: () => '', + // there is a reference to the config api in the options param + load: async _options => { + // returning a lazy loaded plugins and overrides array + // could be util for module federation + return import('./features').then(m => m.default); + }, + }, }); export default app.createRoot(); ``` -## Lazy load your configuration file +### Lazy load your configuration file -In some cases we want to load our configuration from a backend server or we just want to override the default app configuration loader. If you want to do it, you can pass an async callback to the `createApp` function and it should return a promise of the config object: +In some cases we want to load our configuration from a backend server and to do so, you can pass an callback to the `configLoader` option when calling the `createApp` function, the callback should return a promise of an object with the config object: ```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; +import { getConfigFromServer } from './utils'; +// Example lazy loading the app configuration const app = createApp({ - // Lazy loading the plugin feature - features: import('./plugins/MyPlugin').then(m => [m.default]), + // Returns Promise<{ config: ConfigApi }> + configLoader: async () => { + // Calls an async utility method that fetches the config object from the server + const config = await getConfigFromServer(); + // Feel free to manipulate the config object before returning it + // A common example is conditionally modify the config based on the running enviroment + return { config }; + }, }); export default app.createRoot(); ``` - -## Customize or override built-in extensions - -Previously you would customize the application route, components, apis, sidebar, etc. through the code in `App.tsx`. Now we want you to write less code and install more extensions to customize your Backstage instance. See [here](../building-plugins/03-extension-types.md) which types of extensions are available for you to customize your application. diff --git a/docs/frontend-system/building-apps/02-built-in-extensions.md b/docs/frontend-system/building-apps/02-built-in-extensions.md index be89e23c9e..4f8417f74f 100644 --- a/docs/frontend-system/building-apps/02-built-in-extensions.md +++ b/docs/frontend-system/building-apps/02-built-in-extensions.md @@ -38,13 +38,13 @@ This extension is the first extension attached to the extension tree. It is resp #### Inputs -| Name | Description | Type | Optional | Default | Extension creator | -| ------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -| root | The app root element. | [coreExtensionData.reactElement](https://backstage.io/docs/reference/frontend-plugin-api.coreextensiondata) | false | The [`App/Root`](#app-root) extension output. | No creator available, configure or override the [`App/Root`](#app-root) extension. | -| apis | The app apis factories. | [createApiExtension.factoryDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension.factorydataref) | false | See [default apis](#default-apis-extensions). | [createApiExtension](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension) | -| themes | The app themes list. | [createThemeExtension.themeDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension.themedataref) | false | See [default themes](#default-theme-extensions). | [createThemeExtension](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension) | -| components | The app components list. | [createComponentExtension.componentDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension.componentdataref) | - | false | See [default components](#defaults-components-extensions). | [createComponentExtension](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension) | -| translations | The app translations list. | [createTranslationExtension.translationDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension.translationdataref) | - | false | - | [createTranslationExtension](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension) | +| Name | Description | Type | Optional | Default | Extension creator | +| ------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | +| root | The app root element. | [coreExtensionData.reactElement](https://backstage.io/docs/reference/frontend-plugin-api.coreextensiondata) | false | The [`App/Root`](#app-root) extension output. | No creator available, configure or override the [`App/Root`](#app-root) extension. | +| apis | The app apis factories. | [createApiExtension.factoryDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension.factorydataref) | false | See [default apis](#default-apis-extensions). | [createApiExtension](https://backstage.io/docs/reference/frontend-plugin-api.createapiextension) | +| themes | The app themes list. | [createThemeExtension.themeDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension.themedataref) | false | See [default themes](#default-theme-extensions). | [createThemeExtension](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension) | +| components | The app components list. | [createComponentExtension.componentDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension.componentdataref) | false | See [default components](#default-components-extensions). | [createComponentExtension](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension) | +| translations | The app translations list. | [createTranslationExtension.translationDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension.translationdataref) | false | - | [createTranslationExtension](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension) | #### Default theme extensions @@ -59,11 +59,11 @@ Extensions that provides default theme inputs for the `App` extension. Extensions that provides default components inputs for the `App` extension. -| kind | namespace | name | id | -| :--------: | :-------: | :-----------------------------------: | :--------------------: | -| components | app | core.components.progress | `components:app/light` | -| components | app | core.components.notFoundErrorPage | `components:app/dark` | -| components | app | core.components.errorBoundaryFallback | `components:app/dark` | +| kind | namespace | name | id | +| :--------: | :-------: | :-----------------------------------: | :----------------------------------------------------: | +| components | app | core.components.progress | `components:app/core.components.progress` | +| components | app | core.components.notFoundErrorPage | `components:app/core.components.notFoundErrorPage` | +| components | app | core.components.errorBoundaryFallback | `components:app/core.components.errorBoundaryFallback` | #### Default apis extensions @@ -91,7 +91,7 @@ Extensions that provides default apis inputs for the `App` extension. ### App root -This is the extension that creates the app root element, so it renders root level components such as app router, layout. +This is the extension that creates the app root element, so it renders root level components such as app router and layout. #### Inputs @@ -115,10 +115,10 @@ An app root element extension that displays messages posted via the [`AlertApi`] ###### Configurations -| Key | Type | Default value | Description | -| -------------------- | -------------------------------------------------------------------------- | ----------------------------------------- | ---------------------------------------------------------------- | -| `transientTimeoutMs` | number | 5000 | Time in milliseconds to wait before displaying messages | -| `anchorOrigin` | { vertical: 'top' \| 'bottom', horizontal: 'left' \| 'center' \| 'right' } | { vertical: 'top', horizontal: 'center' } | Position on the screen where the message alert will be displayed | +| Key | Type | Default value | Description | +| -------------------- | -------------------------------------------------------------------------- | ----------------------------------------- | ----------------------------------------------------------------- | +| `transientTimeoutMs` | number | 5000 | Time in milliseconds to wait before displaying messages. | +| `anchorOrigin` | { vertical: 'top' \| 'bottom', horizontal: 'left' \| 'center' \| 'right' } | { vertical: 'top', horizontal: 'center' } | Position on the screen where the message alert will be displayed. | ###### Override or disable the extension @@ -178,10 +178,10 @@ Renders a route element for each route received as input and a `NotFoundErrorPag Be careful when overriding this extension, as to do so correctly you must consider these implementation requirements: -- The routing system is managed by more than one extension, and they all use 'react-router' behind the scenes and there are also some utilities that are based on the same library like `useRouteRefParams`. Therefore, you cannot use a different library without causing side effects in these other extensions and helper utilities; -- Don't remove configs or inputs, just extend these things yourself with optional new options, otherwise it will cause breaking changes for extensions like `createPageExtension` that depend on this type of entry, for example; -- Remember to user the route refs for getting paths dynamically, otherwise if an adopter modifies a path through configuration, the route is not going to point to the configured path. -- Adopter expects to be able to customize the `NotFoundErrorPage` component via Components API, you should render this component for routes not configured. +- The routing system is managed by more than one extension, and they all use `react-router` behind the scenes. There are also some utilities that are based on the same `routing` library like `useRouteRefParams`. Therefore, you cannot use a different library without causing side effects in these other extensions and helper utilities; +- Don't remove configs or inputs, just extend these things yourself with optional new options, otherwise it will cause breaking changes for extensions like `createPageExtension` that depend on this type of inputs; +- Remember to user the route refs for getting paths dynamically, otherwise if an adopter modifies a path through configuration, the route is not going to point to the configured path; +- Adopters expect to be able to customize the `NotFoundErrorPage` component via Components API, you should render this component for routes not configured. #### Inputs