docs: migrate getting-started docs to new frontend system

Migrate four documentation pages to use the new frontend system as the
primary content, moving the old frontend system instructions to --old
files following the established convention:

- configure-app-with-plugins: rewrite for auto-discovered plugins
- customize-theme: rewrite for ThemeBlueprint/extension-based theming
- enable-public-entry: make new frontend system the primary content
- quickstart-app-plugin: remove manual sidebar wiring instructions

Each main file gets an info banner linking to the --old variant, and
each --old file gets a banner linking back to the current guide.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-29 21:35:17 +02:00
parent 0336f92de8
commit 7c1b319e74
8 changed files with 1370 additions and 375 deletions
+107
View File
@@ -0,0 +1,107 @@
---
id: enable-public-entry--old
title: Enabling a public entry point (Old Frontend System)
description: A guide for how to experiment with public and protected Backstage app bundles
---
::::info
This documentation is for Backstage apps that still use the old frontend
system. If your app uses the new frontend system, read the
[current guide](./enable-public-entry.md) instead.
::::
# Enable Public Entry (Experimental)
In this tutorial, you will learn how to restrict access to your main Backstage app bundle to authenticated users only.
It is expected that the protected bundle feature will be refined in future development iterations, but for now, here is a simplified explanation of how it works:
Your Backstage app bundle is split into two code entries:
- Public entry point containing login pages;
- There is also a protected main entry point that contains the code for what you see after signing in.
With that, Backstage's cli and backend will detect public entry point and serve it to unauthenticated users, while serving the main, protected entry point only to authenticated users.
## Requirements
- The app needs to be served by the `app-backend` plugin, or this won't work;
- Also it will only work for those using `backstage-cli` to build and serve their Backstage app.
## Step-by-step
1. Create a `index-public-experimental.tsx` in your app `src` folder.
:::note
The filename is a convention, so it is not currently configurable.
:::
2. This file is the public entry point for your application, and it should only contain what unauthenticated users should see:
```tsx title="in packages/app/src/index-public-experimental.tsx"
import ReactDOM from 'react-dom/client';
import { createApp } from '@backstage/app-defaults';
import { AppRouter } from '@backstage/core-app-api';
import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
import {
configApiRef,
discoveryApiRef,
createApiFactory,
} from '@backstage/core-plugin-api';
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
// Notice that this is only setting up what is needed by the sign-in pages
const app = createApp({
// If you have any custom APIs that your sign-in page depends on, you need to add them here
apis: [],
components: {
SignInPage: props => {
return (
<SignInPage
{...props}
providers={['guest']}
title="Select a sign-in method"
/>
);
},
},
});
const App = app.createRoot(
<>
<AlertDisplay transientTimeoutMs={2500} />
<OAuthRequestDialog />
<AppRouter>
{/* This component triggers an authenticated redirect to the main app, while staying on the same URL */}
<CookieAuthRedirect />
</AppRouter>
</>,
);
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```
:::note
The frontend will handle cookie refreshing automatically, so you don't have to worry about it.
:::
3. Let's verify that everything is working locally. From your project root folder, run the following commands to build the app and start the backend:
```sh
# building the app package
yarn workspace app start
# starting the backend api
yarn start-backend
```
4. Visit http://localhost:7007 to see the public app and validate that the _index.html_ response only contains a minimal application.
:::note
Regular app serving will always serve protected apps without authenticating.
:::
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect).
That's it!
+15 -55
View File
@@ -4,6 +4,13 @@ title: Enabling a public entry point
description: A guide for how to experiment with public and protected Backstage app bundles
---
::::info
This documentation is written for the new frontend system, which is the default
in new Backstage apps. If your Backstage app still uses the old frontend system,
read the [old frontend system version of this guide](./enable-public-entry--old.md)
instead.
::::
# Enable Public Entry (Experimental)
In this tutorial, you will learn how to restrict access to your main Backstage app bundle to authenticated users only.
@@ -33,51 +40,21 @@ With that, Backstage's cli and backend will detect public entry point and serve
```tsx title="in packages/app/src/index-public-experimental.tsx"
import ReactDOM from 'react-dom/client';
import { createApp } from '@backstage/app-defaults';
import { AppRouter } from '@backstage/core-app-api';
import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
} from '@backstage/core-components';
import {
configApiRef,
discoveryApiRef,
createApiFactory,
} from '@backstage/core-plugin-api';
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
import { signInPageModule } from './overrides/SignInPage';
import { appModulePublicSignIn } from '@backstage/plugin-app/alpha';
import { createApp } from '@backstage/frontend-defaults';
// Notice that this is only setting up what is needed by the sign-in pages
const app = createApp({
// If you have any custom APIs that your sign-in page depends on, you need to add them here
apis: [],
components: {
SignInPage: props => {
return (
<SignInPage
{...props}
providers={['guest']}
title="Select a sign-in method"
/>
);
},
},
features: [signInPageModule, appModulePublicSignIn],
});
const App = app.createRoot(
<>
<AlertDisplay transientTimeoutMs={2500} />
<OAuthRequestDialog />
<AppRouter>
{/* This component triggers an authenticated redirect to the main app, while staying on the same URL */}
<CookieAuthRedirect />
</AppRouter>
</>,
ReactDOM.createRoot(document.getElementById('root')!).render(
app.createRoot(),
);
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
```
The `signInPageModule` is your custom sign-in page extension that you should already have configured in your app. The `appModulePublicSignIn` from `@backstage/plugin-app/alpha` provides the `CookieAuthRedirect` component that triggers an authenticated redirect to the main app after sign-in.
:::note
The frontend will handle cookie refreshing automatically, so you don't have to worry about it.
:::
@@ -99,20 +76,3 @@ With that, Backstage's cli and backend will detect public entry point and serve
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect).
That's it!
## New Frontend System
If your app uses the new frontend system, you can still use the public entry point feature. The `index-public-experimental.tsx` file does end up looking a bit different in this case:
```tsx title="in packages/app/src/index-public-experimental.tsx"
import ReactDOM from 'react-dom/client';
import { signInPageModule } from './overrides/SignInPage';
import { appModulePublicSignIn } from '@backstage/plugin-app/alpha';
import { createApp } from '@backstage/frontend-defaults';
const app = createApp({
features: [signInPageModule, appModulePublicSignIn],
});
ReactDOM.createRoot(document.getElementById('root')!).render(app.createRoot());
```
@@ -0,0 +1,315 @@
---
id: quickstart-app-plugin--old
title: Adding Custom Plugin to Existing Monorepo App (Old Frontend System)
description: Tutorial for adding a custom plugin to an existing Backstage monorepo application
---
::::info
This documentation is for Backstage apps that still use the old frontend
system. If your app uses the new frontend system, read the
[current guide](./quickstart-app-plugin.md) instead.
::::
###### September 15th 2020 - v0.1.1-alpha.21
<br />
> This document takes you through setting up a new plugin for your existing
> monorepo with a _GitHub provider already setup_. If you don't have either of
> those, you can clone
> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app)
> which this document builds on.
>
> This document does not cover authoring a plugin for sharing with the Backstage
> community. That will have to be a later discussion.
>
> We start with a skeleton plugin install. And after verifying its
> functionality, extend the Sidebar to make our life easy. Finally, we add
> custom code to display GitHub repository information.
>
> This document assumes you have Node.js 16 active along with Yarn and Python.
> Please note, that at the time of this writing, the current version is
> 0.1.1-alpha.21. This guide can still be used with future versions, just,
> verify as you go. If you run into issues, you can compare your setup with mine
> here >
> [simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin).
## The Skeleton Plugin
1. Start by using the built-in creator. From the terminal and root of your
project run: `yarn new` and select `frontend-plugin`.
1. Enter a plugin ID. I used `github-playground`
1. When the process finishes, let's start the backend:
`yarn --cwd packages/backend start`
1. If you see errors starting, refer to
[Auth Configuration](https://backstage.io/docs/auth/) for more information on
environment variables.
1. And now the frontend, from a new terminal window and the root of your
project: `yarn start`
1. As usual, a browser window should popup loading the App.
1. Now manually navigate to our plugin page from your browser:
`http://localhost:3000/github-playground`
1. You should see successful verbiage for this endpoint,
`Welcome to github-playground!`
## The Shortcut
Let's add a shortcut.
1. Open and modify `root: packages > app > src > components > Root.tsx` with the
following:
```tsx
import GitHubIcon from '@material-ui/icons/GitHub';
...
<SidebarItem icon={GitHubIcon} to="github-playground" text="GitHub Repository" />
```
Simple! The App will reload with your changes automatically. You should now see
a GitHub icon displayed in the sidebar. Clicking that will link to our new
plugin. And now, the API fun begins.
## The Identity
Our first modification will be to extract information from the Identity API.
1. Start by opening
`root: plugins > github-playground > src > components > ExampleComponent > ExampleComponent.tsx`
1. Add two new imports
```tsx
// Add identityApiRef to the list of imported from core
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
```
3. Adjust the ExampleComponent from inline to block
_from inline:_
```tsx
const ExampleComponent = () => ( ... )
```
_to block:_
```tsx
const ExampleComponent = () => {
return (
...
)
}
```
4. Now add our hook and const data before the return statement
```tsx
// our API hook
const identityApi = useApi(identityApiRef);
// data to use
const userId = identityApi.getUserId();
const profile = identityApi.getProfile();
```
5. Finally, update the InfoCard's jsx to use our new data
```tsx
<InfoCard title={userId}>
<Typography variant="body1">
{`${profile.displayName} | ${profile.email}`}
</Typography>
</InfoCard>
```
If everything is saved, you should see your name, id, and email on the
github-playground page. Our data accessed is synchronous. So we just grab and
go.
https://github.com/backstage/backstage/tree/master/contrib
6. Here is the entire file for reference
[ExampleComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md)
## The Wipe
The last file we will touch is ExampleFetchComponent. Because of the number of
changes, let's start by wiping this component clean.
1. Start by opening
`root: plugins > github-playground > src > components > ExampleFetchComponent > ExampleFetchComponent.tsx`
1. Replace everything in the file with the following:
```tsx
import useAsync from 'react-use/lib/useAsync';
import Alert from '@material-ui/lab/Alert';
import { Table, TableColumn, Progress } from '@backstage/core-components';
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
import { graphql } from '@octokit/graphql';
export const ExampleFetchComponent = () => {
return <div>Nothing to see yet</div>;
};
```
3. Save that and ensure you see no errors. Comment out the unused imports if
your linter gets in the way.
###### We will add a lot to this file for the sake of ease. Please don't do this in productional code!
## The Graph Model
GitHub has a GraphQL API available for interacting. Let's start by adding our
basic repository query
1. Add the query const statement outside ExampleFetchComponent
```tsx
const query = `{
viewer {
repositories(first: 100) {
totalCount
nodes {
name
createdAt
description
diskUsage
isFork
}
pageInfo {
endCursor
hasNextPage
}
}
}
}`;
```
2. Using this structure as a guide, we will break our query into type parts
3. Add the following outside of ExampleFetchComponent
```tsx
type Node = {
name: string;
createdAt: string;
description: string;
diskUsage: number;
isFork: boolean;
};
type Viewer = {
repositories: {
totalCount: number;
nodes: Node[];
pageInfo: {
endCursor: string;
hasNextPage: boolean;
};
};
};
```
## The Table Model
Using Backstage's own component library, let's define a custom table. This
component will get used if we have data to display.
1. Add the following outside of ExampleFetchComponent
```tsx
type DenseTableProps = {
viewer: Viewer;
};
export const DenseTable = ({ viewer }: DenseTableProps) => {
const columns: TableColumn[] = [
{ title: 'Name', field: 'name' },
{ title: 'Created', field: 'createdAt' },
{ title: 'Description', field: 'description' },
{ title: 'Disk Usage', field: 'diskUsage' },
{ title: 'Fork', field: 'isFork' },
];
return (
<Table
title="List Of User's Repositories"
options={{ search: false, paging: false }}
columns={columns}
data={viewer.repositories.nodes}
/>
);
};
```
## The Fetch
We're ready to flush out our fetch component
1. Add our api hook inside ExampleFetchComponent
```tsx
const auth = useApi(githubAuthApiRef);
```
2. The access token we need to make our GitHub request and the request itself is
obtained in an asynchronous manner.
3. Add the `useAsync` block inside the ExampleFetchComponent
```tsx
const { value, loading, error } = useAsync(async (): Promise<any> => {
const token = await auth.getAccessToken();
const gqlEndpoint = graphql.defaults({
// Uncomment baseUrl if using enterprise
// baseUrl: 'https://github.MY-BIZ.com/api',
headers: {
authorization: `token ${token}`,
},
});
const { viewer } = await gqlEndpoint(query);
return viewer;
}, []);
```
4. The resolved data is conveniently destructured with `value` containing our
Viewer type. `loading` as a boolean, self explanatory. And `error` which is
present only if necessary. So let's use those as the first 3 of 4 multi
return statements.
5. Add the _if return_ blocks below our async block
```tsx
if (loading) return <Progress />;
if (error) return <Alert severity="error">{error.message}</Alert>;
if (value && value.repositories) return <DenseTable viewer={value} />;
```
6. The third line here utilizes our custom table accepting our Viewer type.
7. Finally, we add our _else return_ block to catch any other scenarios.
```tsx
return (
<Table
title="List Of User's Repositories"
options={{ search: false, paging: false }}
columns={[]}
data={[]}
/>
);
```
8. After saving that, and given we don't have any errors, you should see a table
with basic information on your repositories.
9. Here is the entire file for reference
[ExampleFetchComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md)
10. We finished! You should see your own GitHub repository's information
displayed in a basic table. If you run into issues, you can compare the repo
that backs this document,
[simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin)
## Where to go from here
> Break apart ExampleFetchComponent into smaller logical parts contained in
> their own files. Rename your components to something other than ExampleXxx.
>
> You might be really proud of a plugin you develop. Consider sharing it with
> the Backstage community by contributing to the [community-plugins repository](https://github.com/backstage/community-plugins).
+14 -37
View File
@@ -4,35 +4,27 @@ title: Adding Custom Plugin to Existing Monorepo App
description: Tutorial for adding a custom plugin to an existing Backstage monorepo application
---
###### September 15th 2020 - v0.1.1-alpha.21
<br />
::::info
This documentation is written for the new frontend system, which is the default
in new Backstage apps. If your Backstage app still uses the old frontend system,
read the [old frontend system version of this guide](./quickstart-app-plugin--old.md)
instead.
::::
> This document takes you through setting up a new plugin for your existing
> monorepo with a _GitHub provider already setup_. If you don't have either of
> those, you can clone
> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app)
> which this document builds on.
> monorepo with a _GitHub provider already setup_.
>
> This document does not cover authoring a plugin for sharing with the Backstage
> community. That will have to be a later discussion.
>
> We start with a skeleton plugin install. And after verifying its
> functionality, extend the Sidebar to make our life easy. Finally, we add
> custom code to display GitHub repository information.
>
> This document assumes you have Node.js 16 active along with Yarn and Python.
> Please note, that at the time of this writing, the current version is
> 0.1.1-alpha.21. This guide can still be used with future versions, just,
> verify as you go. If you run into issues, you can compare your setup with mine
> here >
> [simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin).
> functionality, we add custom code to display GitHub repository information.
## The Skeleton Plugin
1. Start by using the built-in creator. From the terminal and root of your
project run: `yarn new` and select `frontend-plugin`.
1. Enter a plugin ID. I used `github-playground`
1. Enter a plugin ID. We'll use `github-playground` for this tutorial.
1. When the process finishes, let's start the backend:
`yarn --cwd packages/backend start`
1. If you see errors starting, refer to
@@ -41,27 +33,15 @@ description: Tutorial for adding a custom plugin to an existing Backstage monore
1. And now the frontend, from a new terminal window and the root of your
project: `yarn start`
1. As usual, a browser window should popup loading the App.
1. Now manually navigate to our plugin page from your browser:
1. Now manually navigate to the plugin page from your browser:
`http://localhost:3000/github-playground`
1. You should see successful verbiage for this endpoint,
`Welcome to github-playground!`
## The Shortcut
Let's add a shortcut.
1. Open and modify `root: packages > app > src > components > Root.tsx` with the
following:
```tsx
import GitHubIcon from '@material-ui/icons/GitHub';
...
<SidebarItem icon={GitHubIcon} to="github-playground" text="GitHub Repository" />
```
Simple! The App will reload with your changes automatically. You should now see
a GitHub icon displayed in the sidebar. Clicking that will link to our new
plugin. And now, the API fun begins.
With the new frontend system, plugins are auto-discovered when installed as
dependencies of your `packages/app` package. The plugin was already added there
by `yarn new`, so the route and a sidebar item are available without any manual
wiring in `App.tsx` or `Root.tsx`.
## The Identity
@@ -72,7 +52,6 @@ Our first modification will be to extract information from the Identity API.
1. Add two new imports
```tsx
// Add identityApiRef to the list of imported from core
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
```
@@ -98,10 +77,8 @@ const ExampleComponent = () => {
4. Now add our hook and const data before the return statement
```tsx
// our API hook
const identityApi = useApi(identityApiRef);
// data to use
const userId = identityApi.getUserId();
const profile = identityApi.getProfile();
```