Skip to main content

v1.50.0

These are the release notes for the v1.50.0 release of Backstage.

A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done.

Highlights

BREAKING: Identity token ownership claim removed by default

The auth.omitIdentityTokenOwnershipClaim setting now defaults to true. Backstage user tokens issued by the auth backend will no longer contain the ent claim with the user's ownership entity refs. This means tokens in large organizations no longer risk hitting HTTP header size limits.

To get ownership info for the current user, code should use the userInfo core service. The setting can still be set back to false if needed, but it will be removed entirely in a future release.

BREAKING: Standard Schema replaces createSchemaFromZod

The deprecated createSchemaFromZod helper has been removed from @backstage/frontend-plugin-api. A new configSchema option for createExtension and createExtensionBlueprint accepts direct schema values from any Standard Schema compatible library with JSON Schema support, such as zod v4 or the zod/v4 subpath from zod v3. Note that direct zod v3 schemas are not supported by the new option — use import { z } from 'zod/v4' from the zod v3 package, or upgrade to zod v4.

See the 1.50 migration documentation for more information.

BREAKING: Backstage UI updates

There are several new additions in Backstage UI, including a new Badge component for non-interactive labeling, a RangeSlider for numeric range selection, a CheckboxGroup component, and a showPaginationLabel prop for controlling pagination label visibility in tables. The TableBodySkeleton has been exported for use outside the built-in Table, and SearchAutocomplete now adapts its background based on its parent container. The useTable complete mode now supports disabling pagination via paginationOptions: { type: 'none' }. Tabs now respect prefers-reduced-motion for indicator animations, and form field descriptions are now properly connected to inputs via aria-describedby for screen reader accessibility.

The RangeSlider component was contributed by @AmbrishRamachandiran in #33112.

There are also several breaking changes to note:

  • Header tabs: The tabs prop now uses HeaderNavTabItem[] instead of HeaderTab[]. Tabs render as a <nav> element with links instead of role="tablist". A new activeTabId prop controls which tab is highlighted, with automatic route-based detection when omitted.
  • Header tab href resolution: Tab href values are now resolved through the router context instead of being passed raw. Relative href values are resolved against the current route, and absolute values may be affected by the router's basename configuration.
  • PluginHeader: Removed the toolbarWrapper element. Update custom CSS targeting .bui-PluginHeaderToolbarWrapper to use .bui-PluginHeaderToolbar instead.
  • React 17 dropped: The minimum supported React version is now 18.

Check the BUI Changelog for more details.

BREAKING: Removed deprecated PermissionedRoute

The deprecated PermissionedRoute component has been removed from @backstage/plugin-permission-react. Use RequirePermission instead.

BREAKING: Removed deprecated signal service exports

The deprecated SignalService and DefaultSignalService exports have been removed from @backstage/plugin-signals-node. Use SignalsService and DefaultSignalsService instead.

BREAKING ALPHA: Catalog node deprecated alpha exports removed

Several deprecated exports have been removed from @backstage/plugin-catalog-node/alpha:

  • catalogServiceRef — use the stable export from @backstage/plugin-catalog-node
  • CatalogLocationsExtensionPoint / catalogLocationsExtensionPoint — use the non-alpha equivalents
  • CatalogProcessingExtensionPoint / catalogProcessingExtensionPoint — use the non-alpha equivalents
  • CatalogAnalysisExtensionPoint / catalogAnalysisExtensionPoint — use the non-alpha equivalents
  • CatalogPermissionRuleInput / CatalogPermissionExtensionPoint / catalogPermissionExtensionPoint — use coreServices.permissionsRegistry directly

BREAKING PRODUCERS: Location entity refs and update method

The Location type in @backstage/catalog-client now includes a required entityRef field with the stable entity reference for each registered location. Any code that produces Location objects must now include this field. Location responses from the catalog backend include this new field, and it is filterable via POST /locations/by-query.

A new updateLocation method has been added to both CatalogApi and CatalogService for updating the type and target of an existing location. A corresponding PUT /locations/:id endpoint is now available in the catalog backend. Any code that implements CatalogService must provide this method.

Catalog model layer system (alpha, opt-in)

A new catalog model layer system has been added that allows plugins to declare and extend catalog entity kinds, annotations, labels, tags, and relations using JSON Schema. The new createCatalogModelLayer API in @backstage/catalog-model provides a builder for composing model definitions, and a compileCatalogModel function validates and merges them into a unified model. Built-in entity kinds now include model layer definitions.

On the backend, a new ModelProcessor in the catalog backend validates entities against compiled model schemas, and provideStaticCatalogModel in @backstage/plugin-catalog-node helps provide static models at startup. The @backstage/plugin-scaffolder-common package includes an example scaffolderCatalogModelLayer that declares the Template entity kind.

All of the above is in alpha, and you have to opt into it by actually providing model sources to the model extension point. If you do not - everything continues working the way it always has.

Experimental embedded Postgres for development

Added experimental support for using embedded-postgres as the database for local development. Set backend.database.client to embedded-postgres in your app config to enable this. The embedded-postgres package must be installed as an explicit dependency in your project.

New DialogApi.open() method

A new open method has been added to DialogApi that renders dialogs without any built-in dialog chrome, giving the caller full control over the dialog presentation. This avoids focus trap conflicts that occur when mixing components from different design libraries. The existing show and showModal methods are now deprecated in favor of open.

Plugin pages migrated to Backstage UI

Several plugin pages have been migrated to use Backstage UI components:

  • TechDocs: The index and reader pages now use BUI Header and Container, with the plugin title changed to "Documentation".
  • Notifications: The notifications plugin has been migrated to use Backstage UI. Contributed by @drodil in #33699.
  • Scaffolder actions page: Redesigned with a BUI sidebar list layout, built-in search filtering, and URL-based deep-linking to specific actions.
  • InspectEntityDialog: Migrated from Material UI to Backstage UI components.

Notifications action

Added a new action to get a user's notifications through the actions registry.

Contributed by @drodil in #33831.

App route redirect configuration

The app/routes extension now supports configuring URL redirects through app-config. Redirects are specified as an array of {from, to} path pairs:

app:
extensions:
- app/routes:
config:
redirects:
- from: /old-path
to: /new-path

toError utility function

A new toError utility function is available in @backstage/errors for converting unknown values to ErrorLike objects. If the value is already error-like it is returned as-is, strings are used directly as the error message, and all other values are wrapped. Non-error causes passed to CustomErrorBase are now converted using toError rather than discarded.

FetchMiddlewares.clarifyFailures()

A new FetchMiddlewares.clarifyFailures() middleware has been added to @backstage/core-app-api that replaces the uninformative "TypeError: Failed to fetch" error with a message that includes the request method and URL. This middleware is now included in the default fetch API middleware stack in @backstage/app-defaults.

Auth0 federated logout and session caching

Sign-out now redirects the browser to Auth0's /v2/logout endpoint, clearing the Auth0 session cookie so that the next sign-in creates a new Auth0 session. Previously, only the Backstage session was cleared. Set federatedLogout: true in the Auth0 provider config to additionally clear the upstream IdP session (e.g. Okta, Google).

A new createAuth0Authenticator factory function uses a CacheService to cache Auth0 profile API responses for 1 minute during token refreshes, avoiding rate limits on repeated page refreshes.

Contributed by @UsainBloot in #33718.

Actions registry typed examples

Actions registered via the actions registry now support typed examples with compile-time-checked input and output values that match their schema definitions. A new execute-template action has been added for executing scaffolder templates through the actions registry.

Deprecated bootstrapEnvProxyAgents()

bootstrapEnvProxyAgents() in @backstage/cli-common has been deprecated in favor of Node.js built-in proxy support. Set NODE_USE_ENV_PROXY=1 alongside your HTTP_PROXY/HTTPS_PROXY environment variables instead. See the corporate proxy guide for details.

SCM event translation layers

New SCM event translation layers have been added for Azure DevOps, GitLab, and Bitbucket Cloud. These modules subscribe to their respective webhook events and translate them into generic catalog SCM events via the experimental catalogScmEventsServiceRef, enabling instant catalog reprocessing when repositories are pushed to, renamed, transferred, or deleted.

Contributed by @lokeshkaki in #33361, #33362, and #33410.

AWS RDS IAM authentication

Added support for AWS RDS IAM authentication for PostgreSQL connections. Set connection.type: rds along with host, user, and region in your database configuration to use short-lived IAM tokens instead of a static password. Requires the @aws-sdk/rds-signer package and an IAM role with rds-db:connect permission.

Contributed by @rolandfuszenecker-seon in #33680.

Catalog performance improvements

Several performance improvements have been made to the catalog backend:

  • Entity filter queries now use EXISTS (correlated subquery) patterns instead of IN (subquery), enabling PostgreSQL semi-join optimizations and fixing NOT IN NULL-semantics pitfalls.
  • Search table writes during stitching now sync only changed rows instead of doing a full delete and re-insert, reducing write churn.
  • A deadlock in the catalog processing loop that occurred with multiple replicas has been fixed. The getProcessableEntities method now correctly wraps SELECT ... FOR UPDATE SKIP LOCKED in a transaction.

The deadlock fix was contributed by @walsm232 in #33481.

TypeScript 6 and 7 compatibility

Several changes have been made to improve TypeScript 6 and 7 compatibility. DOM.AsyncIterable has been added to the default lib in the shared TypeScript configuration, enabling standard async iteration support for DOM APIs. The FlattenedMessages type in createTranslationRef has been fixed to avoid excessive type instantiation depth in newer TypeScript versions. The ESLint plugin has been updated to match stricter type checking in TypeScript 6 and up.

Deprecated humanizeEntityRef in favor of Catalog Presentation API

The humanizeEntityRef and humanizeEntity functions have been deprecated in favor of the Catalog Presentation API across multiple plugins. Use useEntityPresentation, EntityDisplayName, or entityPresentationApiRef instead.

The API docs and catalog graph plugins have also been updated to use the Catalog Presentation API for entity display.

Contributed by @AarishMansur in #33838 and @mvanhorn in #33386.

Conditional scaffolder output

Templates can now conditionally include output links and text items using an if property. Items where the if condition evaluates to false are excluded from the task output.

Contributed by @gusevda90 in #33332.

GitHub provider improvements

Fixed a bug where GithubEntityProvider with validateLocationsExist: true and filters.branch configured would always check for the catalog file on the repository's default branch instead of the configured branch.

Contributed by @thomvaill in #33601.

Added automatic retry on temporary errors (like 5XX) to the shared GitHub GraphQL client used by GithubOrgEntityProvider and GithubEntityProvider, improving resilience against intermittent GitHub API failures.

Contributed by @wpessers in #33632.

Scaffolder permission migration

The scaffolder backend now registers permissions through the PermissionsRegistryService instead of the deprecated createPermissionIntegrationRouter, fixing an issue where scaffolder permissions were not visible to RBAC plugins.

Additional fixes and improvements

  • Fixed GitLab project topic filtering by using the correct API parameter. Contributed by @AmateurMind in #33111.
  • Fixed the SearchModal leaving the page in a broken state when closing. Contributed by @fcamgz in #31406.
  • Fixed AwsS3UrlReader failing to read files from S3 buckets configured with custom endpoint hosts. Contributed by @wtravO in #33612.
  • Fixed the MCP OAuth 2.0 Protected Resource Metadata endpoint returning an internal plugin URL. Contributed by @koalaty-code in #33773.
  • Fixed .well-known/oauth-protected-resource resource URL to comply with RFC 9728 Section 7.3. Contributed by @vincentrit in #33855.
  • Fixed a bug in large document indexing logic by using sub-transaction rollbacks. Contributed by @stanislav-c in #31494.
  • Fixed occasional duplication of v5 class name prefix for MUI 5 components. Contributed by @StateFarmIns in #33054.
  • Fixed CIMD redirect URI matching to allow any port for localhost addresses per RFC 8252. Contributed by @Sarabadu in #33446.
  • The MembersListCard now prefers metadata.title over metadata.name when displaying the group membership card. Contributed by @Parsifal-M in #33885.
  • Fixed SingleInstanceGithubCredentialsProvider to return app credentials when getCredentials is called with a bare host URL.
  • Added Kind field to the AboutCard. Tags moved before Type and Lifecycle, Kind placed after them.
  • The unprocessed entities view is now primarily intended for use as a tab within the DevTools plugin.
  • HostDiscovery now logs a warning when backend.baseUrl is set to a localhost address while NODE_ENV is production.

Security Fixes

This release does not contain any security fixes.

Upgrade path

We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for keeping Backstage updated.

Below you can find a list of links and references to help you learn about and start using this new release.

Sign up for our newsletter if you want to be informed about what is happening in the world of Backstage.