Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@backstage/plugin-search-backend-node

backstage294.4kApache-2.01.3.10TypeScript support: included

A library for Backstage backend plugins that want to interact with the search backend plugin

readme

headline

Backstage

English | 한국어 | 中文版 | Français

License CNCF Status Discord Code style Codecov OpenSSF Best Practices OpenSSF Scorecard

What is Backstage?

Backstage is an open source framework for building developer portals. Powered by a centralized software catalog, Backstage restores order to your microservices and infrastructure and enables your product teams to ship high-quality code quickly without compromising autonomy.

Backstage unifies all your infrastructure tooling, services, and documentation to create a streamlined development environment from end to end.

software-catalog

Out of the box, Backstage includes:

  • Backstage Software Catalog for managing all your software such as microservices, libraries, data pipelines, websites, and ML models
  • Backstage Software Templates for quickly spinning up new projects and standardizing your tooling with your organization’s best practices
  • Backstage TechDocs for making it easy to create, maintain, find, and use technical documentation, using a "docs like code" approach
  • Plus, a growing ecosystem of open source plugins that further expand Backstage’s customizability and functionality

Backstage was created by Spotify but is now hosted by the Cloud Native Computing Foundation (CNCF) as an Incubation level project. For more information, see the announcement.

Project roadmap

For information about the detailed project roadmap including delivered milestones, see the Roadmap.

Getting Started

To start using Backstage, see the Getting Started documentation.

Documentation

The documentation of Backstage includes:

Community

To engage with our community, you can use the following resources:

Governance

See the GOVERNANCE.md document in the backstage/community repository.

License

Copyright 2020-2025 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0

Security

Please report sensitive security issues using Spotify's bug-bounty program rather than GitHub.

For further details, see our complete security release process.

changelog

@backstage/plugin-search-backend-node

1.3.10

Patch Changes

1.3.9

Patch Changes

1.3.9-next.1

Patch Changes

1.3.9-next.0

Patch Changes

1.3.8

Patch Changes

1.3.8-next.2

Patch Changes

1.3.8-next.1

Patch Changes

1.3.8-next.0

Patch Changes

1.3.7

Patch Changes

1.3.7-next.1

Patch Changes

1.3.7-next.0

Patch Changes

1.3.6

Patch Changes

1.3.6-next.2

Patch Changes

1.3.6-next.1

Patch Changes

1.3.6-next.0

Patch Changes

1.3.5

Patch Changes

1.3.5-next.3

Patch Changes

1.3.5-next.2

Patch Changes

1.3.5-next.1

Patch Changes

1.3.5-next.0

Patch Changes

1.3.3

Patch Changes

1.3.3-next.2

Patch Changes

1.3.3-next.1

Patch Changes

1.3.3-next.0

Patch Changes

1.3.2

Patch Changes

1.3.2-next.2

Patch Changes

1.3.2-next.1

Patch Changes

1.3.2-next.0

Patch Changes

1.3.0

Minor Changes

  • fc24d9e: Stop using @backstage/backend-tasks as it will be deleted in near future.

Patch Changes

1.2.28-next.3

Patch Changes

1.2.28-next.2

Patch Changes

1.2.28-next.1

Patch Changes

1.2.28-next.0

Patch Changes

1.2.27

Patch Changes

1.2.27-next.1

Patch Changes

1.2.26-next.0

Patch Changes

1.2.24

Patch Changes

1.2.24-next.3

Patch Changes

1.2.24-next.2

Patch Changes

1.2.24-next.1

Patch Changes

1.2.24-next.0

Patch Changes

1.2.22

Patch Changes

1.2.22-next.1

Patch Changes

1.2.22-next.0

Patch Changes

1.2.21

Patch Changes

1.2.21-next.1

Patch Changes

1.2.21-next.0

Patch Changes

1.2.20

Patch Changes

1.2.19

Patch Changes

1.2.18

Patch Changes

1.2.18-next.2

Patch Changes

1.2.18-next.1

Patch Changes

1.2.17-next.0

Patch Changes

1.2.14

Patch Changes

1.2.14-next.3

Patch Changes

1.2.14-next.2

Patch Changes

1.2.14-next.1

Patch Changes

1.2.14-next.0

Patch Changes

1.2.13

Patch Changes

1.2.13-next.2

Patch Changes

1.2.13-next.1

Patch Changes

1.2.13-next.0

Patch Changes

1.2.12

Patch Changes

1.2.12-next.3

Patch Changes

1.2.12-next.2

Patch Changes

1.2.12-next.1

Patch Changes

1.2.12-next.0

Patch Changes

1.2.11

Patch Changes

1.2.11-next.2

Patch Changes

1.2.11-next.1

Patch Changes

1.2.11-next.0

Patch Changes

1.2.10

Patch Changes

1.2.10-next.2

Patch Changes

1.2.9-next.1

Patch Changes

1.2.9-next.0

Patch Changes

1.2.7

Patch Changes

1.2.7-next.3

Patch Changes

1.2.7-next.2

Patch Changes

1.2.7-next.1

Patch Changes

1.2.6-next.0

Patch Changes

1.2.4

Patch Changes

1.2.4-next.2

Patch Changes

1.2.4-next.1

Patch Changes

1.2.4-next.0

Patch Changes

1.2.3

Patch Changes

1.2.3-next.0

Patch Changes

1.2.2

Patch Changes

1.2.2-next.2

Patch Changes

1.2.2-next.1

Patch Changes

1.2.2-next.0

Patch Changes

1.2.1

Patch Changes

1.2.1-next.1

Patch Changes

1.2.1-next.0

Patch Changes

1.2.0

Minor Changes

Patch Changes

1.2.0-next.2

Patch Changes

1.2.0-next.1

Minor Changes

Patch Changes

1.1.5-next.0

Patch Changes

1.1.4

Patch Changes

1.1.4-next.2

Patch Changes

1.1.4-next.1

Patch Changes

1.1.4-next.0

Patch Changes

1.1.3

Patch Changes

1.1.3-next.2

Patch Changes

1.1.3-next.1

Patch Changes

1.1.3-next.0

Patch Changes

1.1.1

Patch Changes

1.1.1-next.2

Patch Changes

1.1.1-next.1

Patch Changes

1.1.1-next.0

Patch Changes

1.1.0

Minor Changes

  • 29ebc43a0b: numberOfResults is now provided alongside the query result
  • dff9843718: The search engine now better handles the case when it receives 0 documents at index-time. Prior to this change, the indexer would replace any existing index with an empty index, effectively deleting it. Now instead, a warning is logged, and any existing index is left alone (preserving the index from the last successful indexing attempt).

Patch Changes

  • a962ce0551: Wait for indexer initialization before finalizing indexing.
  • de8a975911: Changed to use native AbortController and AbortSignal from Node.js, instead of the one from node-abort-controller. This is possible now that the minimum supported Node.js version of the project is 16.

    Note that their interfaces are very slightly different, but typically not in a way that matters to consumers. If you see any typescript errors as a direct result from this, they are compatible with each other in the ways that we interact with them, and should be possible to type-cast across without ill effects.

  • 683ced83f6: Fixed a bug that could cause a max listeners exceeded warning to be logged when more than 10 collators were running simultaneously.

  • 81b1e7b0fe: Updated indexer and decorator base classes to take advantage of features introduced in Node.js v16; be sure you are running a supported version of Node.js.
  • 54c5836f7a: Use of TestPipeline.withSubject() is now deprecated. Instead, use the fromCollator, fromDecorator, or fromIndexer static methods to instantiate a test pipeline. You may also use the class' withCollator, withDecorator, and withIndexer instance methods to build test pipelines that consist of multiple test subjects.
  • Updated dependencies

1.1.0-next.3

Patch Changes

1.1.0-next.2

Minor Changes

  • 29ebc43a0b: numberOfResults is now provided alongside the query result

Patch Changes

1.0.5-next.1

Patch Changes

1.0.5-next.0

Patch Changes

1.0.4

Patch Changes

1.0.4-next.1

Patch Changes

1.0.4-next.0

Patch Changes

1.0.3

Patch Changes

1.0.3-next.2

Patch Changes

1.0.3-next.1

Patch Changes

1.0.3-next.0

Patch Changes

1.0.2

Patch Changes

1.0.2-next.2

Patch Changes

1.0.2-next.1

Patch Changes

1.0.2-next.0

Patch Changes

1.0.1

Patch Changes

1.0.1-next.0

Patch Changes

1.0.0

Major Changes

Patch Changes

0.6.3-next.2

Patch Changes

0.6.3-next.1

Patch Changes

0.6.3-next.0

Patch Changes

0.6.2

Patch Changes

  • e7794a0aaa: propagate indexing errors so they don't appear successful to the task scheduler
  • 3bb25a9acc: Introducing a NewlineDelimitedJsonCollatorFactory, which can be used to create search indices from newline delimited JSON files stored in external storage readable via a configured UrlReader instance.

    This is useful if you have an independent process periodically generating *.ndjson files consisting of IndexableDocument objects and want to be able to generate a fresh index based on the latest version of such a file.

  • 3bb25a9acc: Fixed a bug that prevented TestPipeline.withSubject from identifying valid Readable subjects that were technically transform streams.

  • 915700f64f: The provided search engine now adds a pagination-aware rank value to all results.
  • 7d8acfc32e: Replaced all @beta exports with @public exports
  • Updated dependencies

0.6.2-next.2

Patch Changes

0.6.2-next.1

Patch Changes

0.6.2-next.0

Patch Changes

0.6.1

Patch Changes

  • 71d3432710: Search Engines will now index documents in batches of 1000 instead of 100 (under the hood). This may result in your Backstage backend consuming slightly more memory during index runs, but should dramatically improve indexing performance for large document sets.
  • 3a74e203a8: Support generating highlighted matched terms in search result data
  • Updated dependencies

0.6.1-next.1

Patch Changes

  • 71d3432710: Search Engines will now index documents in batches of 1000 instead of 100 (under the hood). This may result in your Backstage backend consuming slightly more memory during index runs, but should dramatically improve indexing performance for large document sets.
  • 3a74e203a8: Support generating highlighted matched terms in search result data
  • Updated dependencies

0.6.1-next.0

Patch Changes

0.6.0

Minor Changes

  • 0a63e99a26: BREAKING: IndexBuilder.addCollator() now requires a schedule parameter (replacing defaultRefreshIntervalSeconds) which is expected to be a TaskRunner that is configured with the desired search indexing schedule for the given collator.

    Scheduler.addToSchedule() now takes a new parameter object (ScheduleTaskParameters) with two new options id and scheduledRunner in addition to the migrated task argument.

    NOTE: The search backend plugin now creates a dedicated database for coordinating indexing tasks.

    To make this change to an existing app, make the following changes to packages/backend/src/plugins/search.ts:

    +import { Duration } from 'luxon';
    
    /* ... */
    
    +  const schedule = env.scheduler.createScheduledTaskRunner({
    +    frequency: Duration.fromObject({ minutes: 10 }),
    +    timeout: Duration.fromObject({ minutes: 15 }),
    +    initialDelay: Duration.fromObject({ seconds: 3 }),
    +  });
    
       indexBuilder.addCollator({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
       indexBuilder.addCollator({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
       const { scheduler } = await indexBuilder.build();
    -  setTimeout(() => scheduler.start(), 3000);
    +  scheduler.start();
    /* ... */

    NOTE: For scenarios where the lunr search engine is used in a multi-node configuration, a non-distributed TaskRunner like the following should be implemented to ensure consistency across nodes (alternatively, you can configure the search plugin to use a non-distributed DB such as SQLite):

    +import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
    
    /* ... */
    
    +  const schedule: TaskRunner = {
    +    run: async (task: TaskInvocationDefinition) => {
    +      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({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
    /* ... */

Patch Changes

0.6.0-next.1

Minor Changes

  • 0a63e99a26: BREAKING: IndexBuilder.addCollator() now requires a schedule parameter (replacing defaultRefreshIntervalSeconds) which is expected to be a TaskRunner that is configured with the desired search indexing schedule for the given collator.

    Scheduler.addToSchedule() now takes a new parameter object (ScheduleTaskParameters) with two new options id and scheduledRunner in addition to the migrated task argument.

    NOTE: The search backend plugin now creates a dedicated database for coordinating indexing tasks.

    To make this change to an existing app, make the following changes to packages/backend/src/plugins/search.ts:

    +import { Duration } from 'luxon';
    
    /* ... */
    
    +  const schedule = env.scheduler.createScheduledTaskRunner({
    +    frequency: Duration.fromObject({ minutes: 10 }),
    +    timeout: Duration.fromObject({ minutes: 15 }),
    +    initialDelay: Duration.fromObject({ seconds: 3 }),
    +  });
    
       indexBuilder.addCollator({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
       indexBuilder.addCollator({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
       const { scheduler } = await indexBuilder.build();
    -  setTimeout(() => scheduler.start(), 3000);
    +  scheduler.start();
    /* ... */

    NOTE: For scenarios where the lunr search engine is used in a multi-node configuration, a non-distributed TaskRunner like the following should be implemented to ensure consistency across nodes (alternatively, you can configure the search plugin to use a non-distributed DB such as SQLite):

    +import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks';
    
    /* ... */
    
    +  const schedule: TaskRunner = {
    +    run: async (task: TaskInvocationDefinition) => {
    +      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({
    -    defaultRefreshIntervalSeconds: 600,
    +    schedule,
         factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
          discovery: env.discovery,
          tokenManager: env.tokenManager,
         }),
       });
    
    /* ... */

Patch Changes

  • 230ad0826f: Bump to using @types/node v16

0.5.3-next.0

Patch Changes

0.5.2

Patch Changes

0.5.1

Patch Changes

0.5.1-next.0

Patch Changes

0.5.0

Minor Changes

  • 022507c860: BREAKING

    The Backstage Search Platform's indexing process has been rewritten as a stream pipeline in order to improve efficiency and performance on large document sets.

    The concepts of Collator and Decorator have been replaced with readable and transform object streams (respectively), as well as factory classes to instantiate them. Accordingly, the SearchEngine.index() method has also been replaced with a getIndexer() factory method that resolves to a writable object stream.

    Check this upgrade guide for further details.

Patch Changes

0.4.7

Patch Changes

0.4.6

Patch Changes

0.4.5

Patch Changes

0.4.4

Patch Changes

  • 5333451def: Cleaned up API exports

0.4.3

Patch Changes

  • a369f19e7e: Handle special case when filter array has single value optimizing Lunr search behaviour.
  • Updated dependencies

0.4.2

Patch Changes

  • a13f21cdc: Implement optional pageCursor based paging in search.

    To use paging in your app, add a <SearchResultPager /> to your SearchPage.tsx.

  • Updated dependencies

0.4.1

Patch Changes

  • d9c13d535: Implements configuration and indexing functionality for ElasticSearch search engine. Adds indexing, searching and default translator for ElasticSearch and modifies default backend example-app to use ES if it is configured.

    Example configurations:

    AWS

    Using AWS hosted ElasticSearch the only configuration options needed is the URL to the ElasticSearch service. The implementation assumes that environment variables for AWS access key id and secret access key are defined in accordance to the default AWS credential chain..

    search:
      elasticsearch:
        provider: aws
        node: https://my-backstage-search-asdfqwerty.eu-west-1.es.amazonaws.com

    Elastic.co

    Elastic Cloud hosted ElasticSearch uses a Cloud ID to determine the instance of hosted ElasticSearch to connect to. Additionally, username and password needs to be provided either directly or using environment variables like defined in Backstage documentation.

    search:
      elasticsearch:
        provider: elastic
        cloudId: backstage-elastic:asdfqwertyasdfqwertyasdfqwertyasdfqwerty==
        auth:
          username: elastic
          password: changeme

    Others

    Other ElasticSearch instances can be connected to by using standard ElasticSearch authentication methods and exposed URL, provided that the cluster supports that. The configuration options needed are the URL to the node and authentication information. Authentication can be handled by either providing username/password or and API key or a bearer token. In case both username/password combination and one of the tokens are provided, token takes precedence. For more information how to create an API key, see Elastic documentation on API keys and how to create a bearer token, see Elastic documentation on tokens.

    Configuration examples

    With username and password
    search:
      elasticsearch:
        node: http://localhost:9200
        auth:
          username: elastic
          password: changeme
    With bearer token
    search:
      elasticsearch:
        node: http://localhost:9200
        auth:
          bearer: token
    With API key
    search:
      elasticsearch:
        node: http://localhost:9200
        auth:
          apiKey: base64EncodedKey
  • Updated dependencies

0.4.0

Minor Changes

  • 97b2eb37b: Change return value of SearchEngine.index to Promise<void> to support implementation of external search engines.

0.3.0

Minor Changes

  • 9f3ecb555: Build search queries using the query builder in LunrSearchEngine. This removes the support for specifying custom queries with the lunr query syntax, but makes sure that inputs are properly escaped. Supporting the full lunr syntax is still possible by setting a custom query translator. The interface of LunrSearchEngine.setTranslator() is changed to support building lunr queries.

Patch Changes

  • 9f3ecb555: Enhance the search results of LunrSearchEngine to support a more natural search experience. This is done by allowing typos (by using fuzzy search) and supporting typeahead search (using wildcard queries to match incomplete words).
  • 4176a60e5: Change search scheduler from starting indexing in a fixed interval (for example every 60 seconds), to wait a fixed time between index runs. This makes sure that no second index process for the same document type is started when the previous one is still running.

0.2.2

Patch Changes

  • 9c8ea7e24: Handle errors in collators and decorators and log them.
  • 7e7cec86a: Fixed bug preventing searches with filter values containing : from returning results.

0.2.1

Patch Changes

  • 14aad6113: Improved the quality of free text searches in LunrSearchEngine.

0.2.0

Minor Changes

  • 5aff84759: This release represents a move out of a pre-alpha phase of the Backstage Search plugin, into an alpha phase. With this release, you gain more control over the layout of your search page on the frontend, as well as the ability to extend search on the backend to encompass everything Backstage users may want to find.

    If you are updating to version v0.4.0 of @backstage/plugin-search from a prior release, you will need to make modifications to your app backend.

    First, navigate to your backend package and install the two related search backend packages:

    cd packages/backend
    yarn add @backstage/plugin-search-backend @backstage/plugin-search-backend-node

    Wire up these new packages into your app backend by first creating a new search.ts file at src/plugins/search.ts with contents like the following:

    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,
      });
    }

    Then, ensure the search plugin you configured above is initialized by modifying your backend's index.ts file in the following ways:

    +import search from './plugins/search';
    // ...
    +const searchEnv = useHotMemoize(module, () => createEnv('search'));
    // ...
    +apiRouter.use('/search', await search(searchEnv));
    // ...

Patch Changes

  • db1c8f93b: The `<Search...Next /> set of components exported by the Search Plugin are now updated to use the Search Backend API. These will be made available as the default non-"next" versions in a follow-up release.

    The interfaces for decorators and collators in the Search Backend have also seen minor, breaking revisions ahead of a general release. If you happen to be building on top of these interfaces, check and update your implementations accordingly. The APIs will be considered more stable in a follow-up release.

  • Updated dependencies [db1c8f93b]

0.1.4

Patch Changes

  • e1e757569: Introduced Scheduler which is responsible for adding new tasks to a schedule together with it's interval timer as well as starting and stopping the scheduler processes.

0.1.3

Patch Changes

  • b9b2b4b76: Lunr Search Engine support

0.1.2

Patch Changes

  • 4d248725e: Bump to use the in-repo latest backend-common, and the correct version of express-promise-router