【Backstage.io】Creating a Simple iframe Backstage Plugin

Integration Tutorial with n8n by iframe

Jincoco
7 min readJun 4, 2024

Backstage provides a very convenient template generator, allowing users to focus on integrating the necessary systems without worrying about the basic setup of the plugin within Backstage. In theory, Backstage can integrate with any platform or software you want. This article will guide you through writing a Backstage plugin from scratch by using an iframe to simply integrate the n8n application.

What is n8n?

n8n is a free and open-source workflow automation tool. It allows you to design automated workflows through a visual editor and can integrate with various applications and services. This makes n8n a powerful tool for businesses and individual users to achieve process automation.

Step 1: Generate the Plugin Template

In the Backstage project directory, type yarn new. You'll see many default templates available to help you get started quickly. Since this article's approach to integrating n8n via an iframe only involves the frontend, we choose plugin - A new frontend plugin.

For naming conventions of the plugin’s package structure, refer to: https://backstage.io/docs/backend-system/architecture/index#package-structure

The generated plugin module directory structure looks like this, with two main areas of focus for this implementation:

  • components: This folder will contain all frontend components, typically written as React components. These components are responsible for displaying UI elements and handling user interactions within the plugin.
  • plugin.ts: This is the core file of the plugin, responsible for initializing and configuring the plugin. Here, we define the main logic and necessary settings for the plugin. The code in plugin.ts primarily establishes frontend objects to ensure the plugin runs and displays correctly.

Next, we will start the plugin using yarn dev to test its status. Navigate to the plugin path at /backstage-plugin-n8n. You will see the frontend interface of the example components, with the code located in ExampleComponent/ExampleComponent.tsx. As these elements are unnecessary, we can simplify the directory structure as much as possible.

Step 2: Writing the Frontend Page Component

To simplify the development process, I have reduced the necessary files to just Page.tsx and index.ts, and written the code to embed the n8n iframe. This allows us to easily render the n8n plugin. Next, we will export the N8NIndexPage component in index.ts.

  • The Page.tsx file is responsible for defining the N8NIndexPage component and embedding the n8n iframe within the component.
  • The index.ts file is used to export the N8NIndexPage component, making it available for reference and use elsewhere.

This setup streamlines the process of integrating and displaying the n8n plugin.

└── backstage-plugin-n8n
├── dev
├── node_modules
└── src
└── components
├── index.ts
└── Page.tsx
// Page.tsx

import React from 'react';
import { Page, Header, Content } from '@backstage/core-components';

export const N8NIndexPage = () => {

return (
<Page themeId="tool">
<Header title="n8n" subtitle="這是一個n8n嵌入視窗的使用示例,持續開發中... admin@admin.com / @A123123" />
<Content>
<iframe
src="https://un8n.liontravel.com"
title="n8n-iframe"
width="100%"
height="100%"
style={{ border: 0 }}
/>
</Content>
</Page>
);
};
//-------------------------------------------------------------
// index.ts
export { N8NIndexPage} from './Page';

Step 3: Configuring the Frontend Object in plugin.ts

Using plugin.ts, we will set up the frontend component we just created. Since we have changed the file structure, we need to modify the template example accordingly. The result will look like this:

// plugin.ts

import {
createPlugin,
createRoutableExtension,
} from '@backstage/core-plugin-api';

import { rootRouteRef } from './routes';

export const backstagePluginN8NPlugin = createPlugin({
id: 'backstage-plugin-n8n',
routes: {
root: rootRouteRef,
},
});

export const BackstagePluginN8NPage = backstagePluginN8NPlugin.provide(
createRoutableExtension({
name: 'BackstagePluginN8NPage',
component: () =>
import('./components').then(m => m.N8NIndexPage),
mountPoint: rootRouteRef,
}),
);

Completion: Reviewing the Results

Start the development server with yarn dev and navigate to the page /backstage-plugin-n8n to see the results. Without discussing the n8n environment setup for now, you should be able to see that the n8n interface has been successfully integrated into Backstage.

Further Optimization: Different Types of Page Rendering

The Page can be divided into pages under the homepage, which might display the entire list of n8n projects, or in the components' tab, only showing the n8n projects corresponding to those components. The key point is that we should write two different page solutions to meet these two display requirements. Now let's return to Page.tsx to add another frontend component, N8NComponentPage, and modify the exports in index.ts accordingly. Similarly, we need to update plugin.ts to create the new frontend objects and adjust the exports in index.ts.

// Page.tsx
...
export const N8NComponentPage = () => {

return (
<Page themeId="tool">
<Content>
<iframe
src="https://ukuma.liontravel.com"
title="n8n-iframe"
width="100%"
height="100%"
style={{ border: 0 }}
/>
</Content>
</Page>
);
};


// index.ts
export { N8NIndexPage, N8NComponentPage } from './Page';
// plugin.ts
...
export const BackstagePluginN8NComponentPage = backstagePluginN8NPlugin.provide(
createRoutableExtension({
name: 'BackstagePluginN8NComponentPage',
component: () =>
import('./components').then(m => m.N8NComponentPage),
mountPoint: rootRouteRef,
}),
);

// index.ts
export { backstagePluginN8NPlugin, BackstagePluginN8NPage, BackstagePluginN8NComponentPage } from './plugin';

Sure, let’s integrate the N8NIndexPage and N8NComponentPage into Root.tsx and EntityPage.tsx, respectively. This will allow N8NIndexPage to appear in a shortcut section and N8NComponentPage to appear under a tab in the component projects.

// EntityPage.tsx
import { BackstagePluginN8NComponentPagen} from '@internal/backstage-plugin-n8n';
...

const serviceEntityPage = (
<EntityLayout>
<EntityLayout.Route path="/backstage-plugin-n8n" title="n8n">
<N8NComponentPage />
</EntityLayout.Route>
</EntityLayout>
);
// Root.tsx

</SidebarGroup>
...
<SidebarItem icon={AccountTreeIcon} to="backstage-plugin-n8n" text="n8n" />
...
</SidebarGroup>

Now we can see different display modes corresponding to the necessary contexts. Of course, this example is just a demonstration, and there will be many areas that need further optimization in the future.

Further Optimization: Dynamic URL Configuration

To avoid hardcoding URLs in the code, we can configure the n8n service location in app-config.yaml, similar to other plugins. This approach offers greater flexibility. Here are the steps for making these modifications.

Return to Page.tsx in the plugin directory, and modify the original target URL to be read from app-config.yaml by referencing configApi to find the n8n.baseUrl parameter. Mimicking the setup style of other plugins, we also need to add our own n8n configuration and its baseUrl parameter under app-config.yaml.

// Page.tsx
import React from 'react';
import { Page, Header, Content } from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { configApiRef } from '@backstage/core-plugin-api';

export const N8NIndexPage = () => {
const configApi = useApi(configApiRef);
const n8nBaseUrl = configApi.getString('n8n.baseUrl');

return (
<Page themeId="tool">
<Header title="n8n" subtitle="這是一個n8n嵌入視窗的使用示例,持續開發中... admin@admin.com / @A123123" />
<Content>
<iframe
src={n8nBaseUrl}
title="n8n-iframe"
width="100%"
height="100%"
style={{ border: 0 }}
/>
</Content>
</Page>
);
};
...

Anticipated Error:

After you restart, you should see the message “Missing required config value at ‘n8n.baseUrl’ in app-config.yaml.” This indicates that the parameter setting for n8n.baseUrl is not found in the app-config.yaml file. Although the app-config.yaml file has been successfully loaded, the Backstage application requires config.d.ts to set the visibility of its configurations. We can add a file in the plugin directory and write the following code to specify that this parameter setting can be visible to the frontend plugins. For detailed settings, please refer to the official documentation.

If the last step is missing, you will still encounter the same error when starting Backstage. We must include config.d.ts in package.json so that the Backstage application can recognize and load the plugin's configuration schema.

// backstage-plugin-n8n/package.json
...
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.0.0",
"msw": "^1.0.0"
},
"files": [
"dist",
"config.d.ts"
],
"configSchema": "config.d.ts"
}

Afterword:

While writing this article and reproducing these steps, I found that the initial step of creating the frontend plugin can be further simplified. Perhaps naming it directly as n8n instead of backstage-plugin-n8n would be more concise. This article is intended as a conceptual demonstration, and there is still room for improvement in a real production environment.

--

--