Skip to main content
Version: Next

Getting Started with Search

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 instead.

Search functions as a plugin to Backstage, so you will need to use Backstage to use Search.

If you haven't setup Backstage already, start here.

Adding Search to the Frontend

From your Backstage root directory
yarn --cwd packages/app add @backstage/plugin-search @backstage/plugin-search-react

Once installed, the search plugin is automatically available in your app through the default feature discovery. It provides a search page at /search, a search navigation item in the sidebar, and a search modal accessible from the sidebar. For more details and alternative installation methods, see installing plugins.

Configuring the search page

The search page can be configured through app-config.yaml. For example, to disable search result tracking:

app-config.yaml
app:
extensions:
- page:search:
config:
noTrack: true

Search result list items

The search page automatically discovers and uses search result list item extensions provided by installed plugins. For example, the catalog plugin provides a CatalogSearchResultListItem and the TechDocs plugin provides a TechDocsSearchResultListItem. These are automatically registered when the respective plugins are installed.

You can also install additional search result list item extensions using the SearchResultListItemBlueprint from @backstage/plugin-search-react/alpha.

Search filters

Similarly, search filter extensions are automatically discovered. You can add custom filters using the SearchFilterBlueprint or SearchFilterResultTypeBlueprint from @backstage/plugin-search-react/alpha.

Adding Search to the Backend

Add the following plugins into your backend app:

From your Backstage root directory
yarn --cwd packages/backend add @backstage/plugin-search-backend @backstage/plugin-search-backend-module-pg @backstage/plugin-search-backend-module-catalog @backstage/plugin-search-backend-module-techdocs

Then add the following lines:

packages/backend/src/index.ts
const backend = createBackend();

// Other plugins...

// search plugin
backend.add(import('@backstage/plugin-search-backend'));

// search engines
backend.add(import('@backstage/plugin-search-backend-module-pg'));

// search collators
backend.add(import('@backstage/plugin-search-backend-module-catalog'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs'));

backend.start();

With the above setup Search will use the Lunr in-memory Search Engine but if your have Postgres setup as your database then it will use Postgres as your Search Engine. Learn more in the Search Engines documentation.

The above also sets up two Collators for you - Catalog and TechDocs - which will index content from these two locations so that you can easily search them. Learn more in the Collators documentation.

Frontend

The search plugin provides extension points for customizing the search experience through blueprints. You can add custom search result list items, filters, and result type filters.

For example, to create a custom search result list item, use the SearchResultListItemBlueprint from @backstage/plugin-search-react/alpha:

import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';

export const MySearchResultListItem = SearchResultListItemBlueprint.make({
name: 'my-result-item',
params: {
predicate: result => result.type === 'my-custom-type',
component: async () => {
const { MyResultItem } = await import('./components/MyResultItem');
return MyResultItem;
},
},
});

Install this in your app by wrapping it in a frontend module and passing it to createApp:

packages/app/src/search/searchModule.ts
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { MySearchResultListItem } from './MySearchResultListItem';

export const searchCustomizations = createFrontendModule({
pluginId: 'search',
extensions: [MySearchResultListItem],
});
packages/app/src/App.tsx
import { createApp } from '@backstage/frontend-defaults';
import { searchCustomizations } from './search/searchModule';

const app = createApp({
features: [searchCustomizations],
});

export default app.createRoot();

For more advanced customization of the Search frontend, also see how to guides such as How to implement your own Search API and How to customize search results highlighting styling

Backend

Backstage Search isn't a search engine itself, rather, it provides an interface between your Backstage instance and a Search Engine of your choice. Currently, we only support two engines, an in-memory search Engine called Lunr and Elasticsearch. See Search Engines documentation for more information how to configure these in your Backstage instance.

Backstage Search can be used to power search of anything! Plugins like the Catalog offer default collators (e.g. DefaultCatalogCollator) which are responsible for providing documents to be indexed. You can register any number of collators with the IndexBuilder like this:

const indexBuilder = new IndexBuilder({ logger: env.logger, searchEngine });

const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 10 },
timeout: { minutes: 15 },
initialDelay: { seconds: 3 },
});

const everyHourSchedule = env.scheduler.createScheduledTaskRunner({
frequency: { hours: 1 },
timeout: { minutes: 90 },
initialDelay: { seconds: 3 },
});

indexBuilder.addCollator({
schedule: every10MinutesSchedule,
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
tokenManager: env.tokenManager,
}),
});

indexBuilder.addCollator({
schedule: everyHourSchedule,
factory: new MyCustomCollatorFactory(),
});

Backstage Search builds and maintains its index on a schedule. You can change how often the indexes are rebuilt for a given type of document. You may want to do this if your documents are updated more or less frequently. You can do so by configuring a scheduled SchedulerServiceTaskRunner to pass into the schedule value, like this:

const every10MinutesSchedule = env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 10 },
timeout: { minutes: 15 },
initialDelay: { seconds: 3 },
});

indexBuilder.addCollator({
schedule: every10MinutesSchedule,
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
tokenManager: env.tokenManager,
}),
});
Note

if you are using the in-memory Lunr search engine, you probably want to implement a non-distributed SchedulerServiceTaskRunner like the following to ensure consistency if you're running multiple search backend nodes (alternatively, you can configure the search plugin to use a non-distributed database such as SQLite):

import {
SchedulerServiceTaskRunner,
SchedulerServiceTaskInvocationDefinition,
} from '@backstage/backend-plugin-api';

const schedule: SchedulerServiceTaskRunner = {
run: async (task: SchedulerServiceTaskInvocationDefinition) => {
const startRefresh = async () => {
while (!task.signal?.aborted) {
try {
await task.fn(task.signal);
} catch {
// ignore intentionally
}

await new Promise(resolve => setTimeout(resolve, 600 * 1000));
}
};
startRefresh();
},
};

indexBuilder.addCollator({
schedule,
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
tokenManager: env.tokenManager,
}),
});

For more advanced customization of the Search backend, also see how to guides such as How to customize fields in the Software Catalog or TechDocs index