backstage: πŸ› Bug Report: Can't use core-plugin-api in a Processor

πŸ“œ Description

I am creating a new Processor which needs to use useApi hook from the core-plugin-api.

However, once I start my backstage application build fails and throws the following error

/public' directory
[0] <i> [webpack-dev-server] 404s will fallback to '/index.html'
[1] ../backstage/node_modules/@backstage/core-plugin-api/dist/index.esm.js:1
[1] import { createVersionedContext, createVersionedValueMap, useVersionedContext, getOrCreateGlobalSingleton } from '@backstage/version-bridge';
[1] ^^^^^^
[1] 
[1] SyntaxError: Cannot use import statement outside a module
[1]     at Object.compileFunction (node:vm:360:18)
[1]     at wrapSafe (node:internal/modules/cjs/loader:1049:15)
[1]     at Module._compile (node:internal/modules/cjs/loader:1084:27)
[1]     at Module._extensions..js (node:internal/modules/cjs/loader:1174:10)
[1]     at Module.load (node:internal/modules/cjs/loader:998:32)
[1]     at Module._load (node:internal/modules/cjs/loader:839:12)
[1]     at Module.require (node:internal/modules/cjs/loader:1022:19)
[1]     at require (node:internal/modules/cjs/helpers:102:18)

Once I comment out the import of useApi everything builds again.

From the stack trace it seems that there’s an issue within core-plugin-api package? Or aren’t we allowed to use that package in backstage/backend? πŸ€”

πŸ‘ Expected behavior

Build should succeed

πŸ‘Ž Actual Behavior with Screenshots

Check the description.

πŸ‘Ÿ Reproduction steps

  1. create a new processor e.g. ⬇️
import { CatalogProcessor, CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
import { Entity } from '@backstage/catalog-model';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { myApiRef } from 'cool-package';
import { useApi } from '@backstage/core-plugin-api';
import { Logger } from 'winston';

class MyCoolProcessor implements CatalogProcessor {
  private readonly logger: Logger;

  constructor(logger: Logger) {
    this.logger = logger.child({ target: 'MyCoolProcessor' });
  }
  getProcessorName(): string {
    return 'MyCoolProcessor';
  }

  async preProcessEntity(entity: Entity, _location: LocationSpec, _emit: CatalogProcessorEmit): Promise<Entity> {
    const api = useApi(someApiRef);
    // remaining logic

    return entity;
  }
};

export default MyCoolProcessor;

πŸ“ƒ Provide the context for the Bug.

No response

πŸ–₯️ Your Environment

node: v18.7.0
yarn: 1.22.19
cli:  0.22.9 (installed)
backstage:  1.15.0

Dependencies:
  @backstage/app-defaults                               1.4.1
  @backstage/backend-app-api                            0.4.5
  @backstage/backend-common                             0.19.1
  @backstage/backend-dev-utils                          0.1.1
  @backstage/backend-plugin-api                         0.5.3, 0.5.4
  @backstage/backend-tasks                              0.5.4
  @backstage/catalog-client                             1.4.3
  @backstage/catalog-model                              1.4.1
  @backstage/cli-common                                 0.1.12
  @backstage/cli-node                                   0.1.2
  @backstage/cli                                        0.22.9
  @backstage/config-loader                              1.3.2
  @backstage/config                                     1.0.8
  @backstage/core-app-api                               1.9.0
  @backstage/core-components                            0.12.5, 0.13.3
  @backstage/core-plugin-api                            1.5.3
  @backstage/dev-utils                                  1.0.17
  @backstage/errors                                     1.2.1
  @backstage/eslint-plugin                              0.1.3
  @backstage/integration-aws-node                       0.1.5
  @backstage/integration-react                          1.1.15
  @backstage/integration                                1.5.1
  @backstage/plugin-adr-common                          0.2.11
  @backstage/plugin-adr                                 0.6.3
  @backstage/plugin-api-docs                            0.9.6
  @backstage/plugin-app-backend                         0.3.47
  @backstage/plugin-auth-backend                        0.18.5
  @backstage/plugin-auth-node                           0.2.16
  @backstage/plugin-catalog-backend-module-aws          0.2.2
  @backstage/plugin-catalog-backend-module-github       0.3.2
  @backstage/plugin-catalog-backend                     1.11.0
  @backstage/plugin-catalog-common                      1.0.15
  @backstage/plugin-catalog-graph                       0.2.32
  @backstage/plugin-catalog-node                        1.4.0
  @backstage/plugin-catalog-react                       1.8.0
  @backstage/plugin-catalog                             1.12.0
  @backstage/plugin-events-node                         0.2.8
  @backstage/plugin-fossa                               0.2.52
  @backstage/plugin-github-actions                      0.6.1
  @backstage/plugin-github-pull-requests-board          0.1.14
  @backstage/plugin-home-react                          0.1.1
  @backstage/plugin-home                                0.5.4
  @backstage/plugin-kubernetes-backend                  0.11.2
  @backstage/plugin-kubernetes-common                   0.6.5
  @backstage/plugin-kubernetes                          0.9.3
  @backstage/plugin-org                                 0.6.10
  @backstage/plugin-permission-backend                  0.5.22
  @backstage/plugin-permission-common                   0.7.7
  @backstage/plugin-permission-node                     0.7.10
  @backstage/plugin-permission-react                    0.4.14
  @backstage/plugin-proxy-backend                       0.2.41
  @backstage/plugin-scaffolder-common                   1.3.2
  @backstage/plugin-search-backend-module-catalog       0.1.3
  @backstage/plugin-search-backend-module-elasticsearch 1.3.2
  @backstage/plugin-search-backend-module-techdocs      0.1.3
  @backstage/plugin-search-backend-node                 1.2.3
  @backstage/plugin-search-backend                      1.3.3
  @backstage/plugin-search-common                       1.2.5
  @backstage/plugin-search-react                        1.6.3
  @backstage/plugin-search                              1.3.3
  @backstage/plugin-sonarqube-backend                   0.2.1
  @backstage/plugin-sonarqube-react                     0.1.7
  @backstage/plugin-sonarqube                           0.7.1
  @backstage/plugin-tech-insights-backend-module-jsonfc 0.1.31
  @backstage/plugin-tech-insights-backend               0.5.13
  @backstage/plugin-tech-insights-common                0.2.11
  @backstage/plugin-tech-insights-node                  0.4.5
  @backstage/plugin-tech-insights                       0.3.12
  @backstage/plugin-techdocs-backend                    1.6.4
  @backstage/plugin-techdocs-module-addons-contrib      1.0.15
  @backstage/plugin-techdocs-node                       1.7.3
  @backstage/plugin-techdocs-react                      1.1.8
  @backstage/plugin-techdocs                            1.6.5
  @backstage/plugin-user-settings                       0.7.5
  @backstage/release-manifests                          0.0.9
  @backstage/test-utils                                 1.4.1
  @backstage/theme                                      0.2.19, 0.4.1
  @backstage/types                                      1.1.0
  @backstage/version-bridge                             1.0.4

πŸ‘€ Have you spent some time to check if this bug has been raised before?

  • I checked and didn’t find similar issue

🏒 Have you read the Code of Conduct?

Are you willing to submit PR?

Yes I am willing to submit a PR!

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Comments: 16 (8 by maintainers)

Most upvoted comments

Could be that that client should ideally be moved to @backstage/plugin-tech-insights-common, right? With a polymorphic copy of the identity API, similar to what the catalog client does with its injected api dependencies https://github.com/backstage/backstage/blob/cef192ebe3a2a3aae86437a923e401c06d7a5a8b/packages/catalog-client/src/types/discovery.ts#L18

That would be a nice contribution, I think πŸ™

Hey πŸ‘‹ so I think what you will want to do is you’ll need to create MyAwesomeClient in an isomorphic way, so that it is compatible with both identityApi implementations. So you could either do private readonly identityApi: ClientIdentityApi | ServerIdentityApi or you can basically just define the interface you actually need like private readonly identityApi: { getProfileInfo: () => Promise<...> } instead and then when you create the instance of MyAwesomeClient you can create a small wrapper around the different implementations of the identityApi which is being used.

What do you need the identityApi for in the frontend and backend?

@miniengineer so you can’t use useApi backend plugins, as they’re frontend only.

What I mean by moving it to the constructor is something like this:

class CoolProcessor implements CatalogProcessor {
  private readonly logger: Logger;
  private readonly entityDataApi: any;

  constructor(logger: Logger, entityDataApi: EntityDataAPI) {
    this.logger = logger.child({ target: 'CoolProcessor' });
    this.entityDataApi = entityDataApi;
  }

And then when you do new CoolProcessor() you would also pass in new EntityDataAPi() or something.

What you will need to do is move the client into a -common package instead of a plugin package which will mean you can share code between both frontend and backend, similar to how the packages/catalog-client is setup in this repo.

If you run yarn new and select plugin-common it should create a barebones package for you to export your client from, and then you can use this code to back the apiRef in the frontend and call new EntityDataAPI() to setup your Processor.

Does this make more sense? πŸ™

Hey πŸ‘‹

So core-plugin-api is a frontend only package. What apiRef are you wanting to consume in the backend?

We normally pass in the dependencies to the constructor when creating the processor before it get’s passed into the CatalogBuilder so you can create whatever you need before rather than calling useApi in the processor methods themselves.