Q&A brings a shared knowledge space into Backstage. It works like a light Stack Overflow for your org. People ask and answer questions. Teams write short articles. You can attach links to catalog entities. Tags and mentions route questions to experts. Votes surface helpful answers. Notifications reach followers of tags, entities, collections, and users. Search shows Q&A next to docs and services. You can group posts into collections. Templates guide repeat requests.
Optional AI can draft answers or suggest tags. The result is faster support for engineers and less noise in chat. Good for on call handoffs, service ownership, and onboarding.
Real teams use it today. The adopters list includes OP Financial Group and QuintoAndar. If your Backstage serves many teams, Q&A gives them a simple place to ask, answer, and share fixes where they already work.
Installation Instructions
These instructions apply to self-hosted Backstage only.
Install the backend
New backend system
- Add the backend package
yarn workspace backend add @drodil/backstage-plugin-qeta-backend
- Register the backend plugin
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// other plugins
backend.add(import('@drodil/backstage-plugin-qeta-backend'));
backend.start();
Old backend system
Version 2 or later needs the new backend system. If you still use the old system, pin version 1.24.5.
- Add the pinned backend package
yarn workspace backend add @drodil/[email protected]
- Create the router
// packages/backend/src/plugins/qeta.ts
import {
createRouter,
DatabaseQetaStore,
} from '@drodil/backstage-plugin-qeta-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
database,
identity,
config,
}: PluginEnvironment) {
const db = await DatabaseQetaStore.create({ database });
return await createRouter({
logger,
database: db,
identity,
config,
});
}
- Mount the router
// packages/backend/src/index.ts
import qeta from './plugins/qeta';
// inside createEnv and apiRouter setup
const qetaEnv = useHotMemoize(module, () => createEnv('qeta'));
apiRouter.use('/qeta', await qeta(qetaEnv));
Install the frontend
- Add the frontend packages
yarn workspace app add @drodil/backstage-plugin-qeta @drodil/backstage-plugin-qeta-react
- Add the route so users can open Q and A
// packages/app/src/App.tsx
import React from 'react';
import { FlatRoutes, Route } from '@backstage/core-app-api';
import { QetaPage } from '@drodil/backstage-plugin-qeta';
export const AppRoutes = () => (
<FlatRoutes>
{/* other routes */}
<Route path="/qeta" element={<QetaPage title="Questions" />} />
</FlatRoutes>
);
- Add a sidebar item
// packages/app/src/components/Root/Root.tsx
import React, { PropsWithChildren } from 'react';
import LiveHelpIcon from '@material-ui/icons/LiveHelp';
import { SidebarItem, SidebarPage } from '@backstage/core-components';
export const Root = ({ children }: PropsWithChildren<{}>) => (
<SidebarPage>
{/* other items */}
<SidebarItem icon={LiveHelpIcon} to="qeta" text="Q&A" />
{children}
</SidebarPage>
);
Add Q and A to catalog entity pages
- Create a content component
// packages/app/src/components/catalog/EntityPage/content/QetaContent.tsx
import React from 'react';
import { useEntity } from '@backstage/plugin-catalog-react';
import { Container } from '@material-ui/core';
import { stringifyEntityRef } from '@backstage/catalog-model';
import { PostsContainer } from '@drodil/backstage-plugin-qeta-react';
export const QetaContent = () => {
const { entity } = useEntity();
return (
<Container>
<PostsContainer
entity={stringifyEntityRef(entity)}
showTitle
showAskButton
type="question" // or "article"
/>
</Container>
);
};
- Mount it as a tab on your entity page
// packages/app/src/components/catalog/EntityPage.tsx
<EntityLayout.Route path="/qeta" title="Q&A">
<QetaContent />
</EntityLayout.Route>
Optional grid view
import { PostsGrid } from '@drodil/backstage-plugin-qeta-react';
// inside QetaContent return
<PostsGrid entity={stringifyEntityRef(entity)} showTitle showAskButton type="question" />
Add Q and A to the home page
// packages/app/src/components/home/HomePage.tsx
import React from 'react';
import { QuestionTableCard } from '@drodil/backstage-plugin-qeta';
import { CustomHomepageGrid } from '@backstage/plugin-home';
import { Content, Page } from '@backstage/core-components';
export const HomePage = (
<Page themeId="home">
<Content>
<CustomHomepageGrid>
<QuestionTableCard />
</CustomHomepageGrid>
</Content>
</Page>
);
Show Q and A results in search
- Render the result item
// packages/app/src/components/search/SearchPage.tsx
import React from 'react';
import { Page } from '@backstage/core-components';
import { SearchBar, SearchContextProvider, SearchResult } from '@backstage/plugin-search-react';
import { QetaSearchResultListItem } from '@drodil/backstage-plugin-qeta';
export const SearchPage = (
<Page themeId="home">
<SearchContextProvider>
<SearchBar />
<SearchResult>
<QetaSearchResultListItem />
</SearchResult>
</SearchContextProvider>
</Page>
);
- Add the search indexing module on the backend
yarn workspace backend add @drodil/backstage-plugin-search-backend-module-qeta
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
import { searchPlugin } from '@backstage/plugin-search-backend/alpha';
const backend = createBackend();
backend.add(searchPlugin());
backend.add(import('@drodil/backstage-plugin-search-backend-module-qeta'));
backend.start();
Configure the plugin
Add minimal config. Defaults work if you do not set these.
# app-config.yaml
qeta:
permissions: false
notifications: true
storage:
type: database
You can tune limits and features
qeta:
allowAnonymous: false
entities:
kinds: [Component]
max: 3
tags:
allowCreation: true
max: 5
mentions:
supportedKinds:
- User
- Group
storage:
disabled: false
allowedMimeTypes:
- png
- jpg
- jpeg
- gif
Optional AI features
Use the OpenAI backend module if you want AI answers or summaries.
- Install the module
yarn workspace backend add @drodil/backstage-plugin-qeta-backend-module-openai
- Register it
// packages/backend/src/index.ts
import { createBackend } from '@backstage/backend-defaults';
const backend = createBackend();
// other plugins
backend.add(import('@drodil/backstage-plugin-qeta-backend-module-openai'));
backend.start();
- Configure the key and options
# app-config.yaml
qeta:
openai:
apiKey: ${OPENAI_API_KEY}
model: gpt-4o-mini
temperature: 0.5
maxTokens: 100
answer:
newQuestions: true
existingQuestions: true
articleSummary: false
post:
tagSuggestions: false
You can also build your own AI handler with the qetaAIExtensionPoint
in a custom backend module.
// example backend module
import { createBackendModule } from '@backstage/backend-plugin-api';
import { qetaAIExtensionPoint } from '@drodil/backstage-plugin-qeta-node';
export const qetaAiModule = createBackendModule({
pluginId: 'qeta',
moduleId: 'custom-ai',
register(reg) {
reg.registerInit({
deps: { ai: qetaAIExtensionPoint },
async init({ ai }) {
ai.setAIHandler({
async answerExistingQuestion(question) {
return { answer: 'Answer' };
},
async answerNewQuestion(title, content) {
return { answer: 'Answer' };
},
async summarizeArticle(article) {
return { answer: 'Summary' };
},
async suggestTags(title, content) {
return { tags: ['example'] };
},
});
},
});
},
});
Then add your module to the backend
backend.add(import('./path/to/qetaAiModule'));
Notes on databases
The plugin works with Postgres and sqlite. It uses your Backstage database connection. No extra setup needed for a basic install.
Summary of what you added
- Backend plugin registered so the API runs under your backend
- Frontend page mounted at path qeta with a sidebar link
- Optional entity tab, home card, and search result item so users see Q and A in common places
- Optional search indexing module for better search
- Optional AI module for answers and summaries
Changelog
This changelog is produced from commits made to the Q&A plugin since 14 days ago, and based on the code located here. It may not contain information about all commits. Releases and version bumps are intentionally omitted. This changelog is generated by AI.
Features
- Improve icon fetching. Backend fetches the favicon binary and sends it to the UI. New or edited links store the favicon. Favicons use data URLs. Adds a favicon field to post data. #339 10 days ago
Bug fixes
- Fix link page title. #343 4 days ago
Improvements
- Improve error logging for link metadata extraction. Add logger warnings. Use the logger in helper functions. #340 6 days ago
Migrations
Run database migrations after upgrade.
Set up Backstage in minutes with Roadie
Focus on using Backstage, rather than building and maintaining it.