diff --git a/packages/create-app/package.json b/packages/create-app/package.json
index a0fb9e6d4a..c69354044d 100644
--- a/packages/create-app/package.json
+++ b/packages/create-app/package.json
@@ -64,6 +64,8 @@
"@backstage/plugin-rollbar-backend": "*",
"@backstage/plugin-scaffolder": "*",
"@backstage/plugin-search": "*",
+ "@backstage/plugin-search-backend": "*",
+ "@backstage/plugin-search-backend-node": "*",
"@backstage/plugin-scaffolder-backend": "*",
"@backstage/plugin-tech-radar": "*",
"@backstage/plugin-techdocs": "*",
diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts
index 2a133b4bf8..a0f646747b 100644
--- a/packages/create-app/src/lib/versions.ts
+++ b/packages/create-app/src/lib/versions.ts
@@ -58,6 +58,8 @@ import { version as pluginRollbarBackend } from '../../../../plugins/rollbar-bac
import { version as pluginScaffolder } from '../../../../plugins/scaffolder/package.json';
import { version as pluginScaffolderBackend } from '../../../../plugins/scaffolder-backend/package.json';
import { version as pluginSearch } from '../../../../plugins/search/package.json';
+import { version as pluginSearchBackend } from '../../../../plugins/search-backend/package.json';
+import { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';
import { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';
import { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';
import { version as pluginTechdocsBackend } from '../../../../plugins/techdocs-backend/package.json';
@@ -89,6 +91,8 @@ export const packageVersions = {
'@backstage/plugin-scaffolder': pluginScaffolder,
'@backstage/plugin-scaffolder-backend': pluginScaffolderBackend,
'@backstage/plugin-search': pluginSearch,
+ '@backstage/plugin-search-backend': pluginSearchBackend,
+ '@backstage/plugin-search-backend-node': pluginSearchBackendNode,
'@backstage/plugin-tech-radar': pluginTechRadar,
'@backstage/plugin-techdocs': pluginTechdocs,
'@backstage/plugin-techdocs-backend': pluginTechdocsBackend,
diff --git a/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx
new file mode 100644
index 0000000000..f525be80a2
--- /dev/null
+++ b/packages/create-app/templates/default-app/packages/app/src/components/search/SearchPage.tsx
@@ -0,0 +1,86 @@
+import React from 'react';
+import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
+
+import { Content, Header, Page } from '@backstage/core';
+import { CatalogResultListItem } from '@backstage/plugin-catalog';
+import {
+ SearchBar,
+ SearchFilter,
+ SearchResult,
+ DefaultResultListItem,
+} from '@backstage/plugin-search';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ bar: {
+ padding: theme.spacing(1, 0),
+ },
+ filters: {
+ padding: theme.spacing(2),
+ },
+ filter: {
+ '& + &': {
+ marginTop: theme.spacing(2.5),
+ },
+ },
+}));
+
+const SearchPage = () => {
+ const classes = useStyles();
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {({ results }) => (
+
+ {results.map(({ type, document }) => {
+ switch (type) {
+ case 'software-catalog':
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+ })}
+
+ )}
+
+
+
+
+
+ );
+};
+
+export const searchPage = ;
diff --git a/packages/create-app/templates/default-app/packages/backend/package.json.hbs b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
index 4b493a32c3..8f5a116de4 100644
--- a/packages/create-app/templates/default-app/packages/backend/package.json.hbs
+++ b/packages/create-app/templates/default-app/packages/backend/package.json.hbs
@@ -27,6 +27,8 @@
"@backstage/plugin-catalog-backend": "^{{version '@backstage/plugin-catalog-backend'}}",
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
+ "@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
+ "@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
"@gitbeaker/node": "^30.2.0",
"@octokit/rest": "^18.5.3",
diff --git a/packages/create-app/templates/default-app/packages/backend/src/index.ts b/packages/create-app/templates/default-app/packages/backend/src/index.ts
index aebd034aae..3d8849fa75 100644
--- a/packages/create-app/templates/default-app/packages/backend/src/index.ts
+++ b/packages/create-app/templates/default-app/packages/backend/src/index.ts
@@ -25,6 +25,7 @@ import catalog from './plugins/catalog';
import scaffolder from './plugins/scaffolder';
import proxy from './plugins/proxy';
import techdocs from './plugins/techdocs';
+import search from './plugins/search';
import { PluginEnvironment } from './types';
function makeCreateEnv(config: Config) {
@@ -57,6 +58,7 @@ async function main() {
const authEnv = useHotMemoize(module, () => createEnv('auth'));
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
+ const searchEnv = useHotMemoize(module, () => createEnv('search'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
const apiRouter = Router();
@@ -65,6 +67,7 @@ async function main() {
apiRouter.use('/auth', await auth(authEnv));
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/proxy', await proxy(proxyEnv));
+ apiRouter.use('/search', await search(searchEnv));
apiRouter.use(notFoundHandler());
const service = createServiceBuilder(module)
diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts
new file mode 100644
index 0000000000..248ed37ea8
--- /dev/null
+++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/search.ts
@@ -0,0 +1,38 @@
+import { useHotCleanup } from '@backstage/backend-common';
+import { createRouter } from '@backstage/plugin-search-backend';
+import {
+ IndexBuilder,
+ LunrSearchEngine,
+} from '@backstage/plugin-search-backend-node';
+import { PluginEnvironment } from '../types';
+import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
+
+export default async function createPlugin({
+ logger,
+ discovery,
+}: PluginEnvironment) {
+ // Initialize a connection to a search engine.
+ const searchEngine = new LunrSearchEngine({ logger });
+ const indexBuilder = new IndexBuilder({ logger, searchEngine });
+
+ // Collators are responsible for gathering documents known to plugins. This
+ // particular collator gathers entities from the software catalog.
+ indexBuilder.addCollator({
+ defaultRefreshIntervalSeconds: 600,
+ collator: new DefaultCatalogCollator({ discovery }),
+ });
+
+ // The scheduler controls when documents are gathered from collators and sent
+ // to the search engine for indexing.
+ const { scheduler } = await indexBuilder.build();
+
+ // A 3 second delay gives the backend server a chance to initialize before
+ // any collators are executed, which may attempt requests against the API.
+ setTimeout(() => scheduler.start(), 3000);
+ useHotCleanup(module, () => scheduler.stop());
+
+ return await createRouter({
+ engine: indexBuilder.getSearchEngine(),
+ logger,
+ });
+}