Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConfigService Injection Issue in Nx/NestJS Library #29136

Open
1 of 4 tasks
ziyadev opened this issue Dec 1, 2024 · 8 comments
Open
1 of 4 tasks

ConfigService Injection Issue in Nx/NestJS Library #29136

ziyadev opened this issue Dec 1, 2024 · 8 comments
Assignees
Labels
scope: node Issues related to Node, Express, NestJS support for Nx type: bug

Comments

@ziyadev
Copy link

ziyadev commented Dec 1, 2024

Current Behavior

I created a new Nx-based NestJS library and used the ConfigService from @nestjs/config. The ConfigModule is imported in the library, and the ConfigService is injected into one of the services. However, when I use this service in a consuming NestJS app, I encounter the following error:

[Nest] 21784  - 12/01/2024, 7:33:51 AM   ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'get')

TypeError: Cannot read properties of undefined (reading 'get')
    at EnvService.getServerEnv (/Users/ziyadev/Desktop/repos/fannaq/dist/apps/testing/webpack:/packages/env/src/nestjs/env.service.ts:12:38)

This indicates that ConfigService is not properly injected, even though ConfigModule has been imported both in the library and as a global module in the app.

Expected Behavior

The ConfigService should be correctly injected into the library service, and configuration values should be accessible without errors.

GitHub Repo

No response

Steps to Reproduce

Nx Report

Node           : 20.15.1
OS             : darwin-x64
Native Target  : x86_64-macos

nx (global)        : 20.0.0
nx                 : 20.0.0
@nx/nest           : 20.0.0
@nx/workspace      : 20.0.0
@nestjs/config     : 2.3.1
typescript         : 5.5.4

Failure Logs

> nx run testing:build:development

asset main.js 11.6 KiB [emitted] (name: main) 1 related asset
asset assets/.gitkeep 0 bytes [emitted] [from: apps/testing/src/assets/.gitkeep] [copied]
orphan modules 2.77 KiB [orphan] 4 modules
runtime modules 670 bytes 3 modules
built modules 8.16 KiB [built]
  modules by path ./ 8.04 KiB
    modules by path ./apps/testing/src/app/*.ts 4.56 KiB
      ./apps/testing/src/app/app.module.ts 1.34 KiB [built] [code generated]
      ./apps/testing/src/app/app.controller.ts 1.6 KiB [built] [code generated]
      ./apps/testing/src/app/app.service.ts 1.62 KiB [built] [code generated]
    ./apps/testing/src/main.ts 665 bytes [built] [code generated]
    ./packages/env/src/nestjs/index.ts + 4 modules 2.83 KiB [not cacheable] [built] [code generated]
  modules by path external "@nestjs/ 126 bytes
    external "@nestjs/common" 42 bytes [built] [code generated]
    external "@nestjs/core" 42 bytes [built] [code generated]
    external "@nestjs/config" 42 bytes [built] [code generated]
webpack 5.96.1 compiled successfully in 7317 ms

———————————————————————————————————————————————————————————————————————————————————————————————————————————————

 NX   Successfully ran target build for project testing (13s)

Debugger listening on ws://localhost:9229/d9d08e05-e76b-4972-98e9-200ab780f2ab

Debugger listening on ws://localhost:9229/d9d08e05-e76b-4972-98e9-200ab780f2ab
For help, see: https://nodejs.org/en/docs/inspector

[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [NestFactory] Starting Nest application...
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [InstanceLoader] EnvModule dependencies initialized +25ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [InstanceLoader] ConfigHostModule dependencies initialized +3ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +1ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [RoutesResolver] AppController {/api}: +11ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [RouterExplorer] Mapped {/api, GET} route +4ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG [NestApplication] Nest application successfully started +3ms
[Nest] 21784  - 12/01/2024, 7:33:41 AM     LOG 🚀 Application is running on: http://localhost:3000/api
[Nest] 21784  - 12/01/2024, 7:33:50 AM     LOG [EnvService] undefined
[Nest] 21784  - 12/01/2024, 7:33:51 AM   ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'get')

TypeError: Cannot read properties of undefined (reading 'get')
    at EnvService.getServerEnv (/Users/ziyadev/Desktop/repos/fannaq/dist/apps/testing/webpack:/packages/env/src/nestjs/env.service.ts:12:38)
    at AppService.getData (/Users/ziyadev/Desktop/repos/fannaq/dist/apps/testing/webpack:/apps/testing/src/app/app.service.ts:12:32)
    at AppController.getData (/Users/ziyadev/Desktop/repos/fannaq/dist/apps/testing/webpack:/apps/testing/src/app/app.controller.ts:11:28)
    at /Users/ziyadev/Desktop/repos/fannaq/node_modules/@nestjs/core/router/router-execution-context.js:38:29
    at InterceptorsConsumer.intercept (/Users/ziyadev/Desktop/repos/fannaq/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:12:20)
    at /Users/ziyadev/Desktop/repos/fannaq/node_modules/@nestjs/core/router/router-execution-context.js:46:60
    at /Users/ziyadev/Desktop/repos/fannaq/node_modules/@nestjs/core/router/router-proxy.js:9:23
    at Layer.handle [as handle_request] (/Users/ziyadev/Desktop/repos/fannaq/node_modules/express/lib/router/layer.js:95:5)
    at next (/Users/ziyadev/Desktop/repos/fannaq/node_modules/express/lib/router/route.js:149:13)
    at Route.dispatch (/Users/ziyadev/Desktop/repos/fannaq/node_modules/express/lib/router/route.js:119:3)

Package Manager Version

No response

Operating System

  • macOS
  • Linux
  • Windows
  • Other (Please specify)

Additional Information

No response

@ziyadev
Copy link
Author

ziyadev commented Dec 1, 2024

The issue seems to be related to the SWC compiler. In the webpack.config.js, changing the compiler option from swc to tsc resolves the problem:

...
NxAppWebpackPlugin({
      target: 'node',
      compiler: 'tsc', // Switched from 'swc' to 'tsc' and the issue is gone
     ...
 
    }),

@ndcunningham
Copy link
Contributor

Hi, thanks for reporting this issue.

Can you provide a small repro highlighting the issue?

NestJS has a specific requirement for monorepos, it could be that we missed something for NxAppWebpackPlugin

@ndcunningham ndcunningham self-assigned this Dec 3, 2024
@ziyadev
Copy link
Author

ziyadev commented Dec 5, 2024

Here’s a small repro along with the NxAppWebpackPlugin configuration I’m using:

const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
const { join } = require('node:path');

module.exports = {
  output: {
    path: join(__dirname, '../../dist/apps/testing'),
  },
  plugins: [
    new NxAppWebpackPlugin({
      target: 'node',
      compiler: 'tsc',
      main: './src/main.ts',
      tsConfig: './tsconfig.app.json',
      assets: ['./src/assets'],
      optimization: false,
      outputHashing: 'none',
      generatePackageJson: false,
      progress: false,
    }),
  ],
};

project.json:

"targets": {
    "build": {
      "executor": "@nx/webpack:webpack",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
        "outputPath": "dist/apps/testing",
        "webpackConfig": "apps/testing/webpack.config.cjs",
        "main": "apps/testing/src/main.ts",
        "tsConfig": "apps/testing/tsconfig.app.json",
        "assets": ["apps/testing/src/assets"],
        "inspect": true
      },
      "configurations": {
        "development": {},
        "production": {}
      }
    },
    "serve": {
      "executor": "@nx/js:node",
      "defaultConfiguration": "development",
      "dependsOn": [
        "build",
        {
          "dependencies": true,
          "target": "build"
        }
      ],
      "options": {
        "buildTarget": "testing:build",
        "runBuildTargetDependencies": true
      },
      "configurations": {
        "development": {
          "buildTarget": "testing:build:development"
        },
        "production": {
          "buildTarget": "testing:build:production"
        }
      }
    }
  }

@eXist-FraGGer
Copy link

eXist-FraGGer commented Jan 10, 2025

Same problem, with rspack.

project.json

{
  "name": "auth",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "{projectRoot}/src",
  "projectType": "application",
  "tags": [],
  "targets": {
    "build": {
      "executor": "@nx/rspack:rspack",
      "cache": true,
      "options": {
        "target": "node",
        "outputPath": "{workspaceRoot}/build/apps/auth",
        "outputHashing": "none",
        "main": "{projectRoot}/src/main.ts",
        "tsConfig": "{projectRoot}/tsconfig.app.json",
        "rspackConfig": "{projectRoot}/rspack.config.ts",
        "assets": [],
        "generatePackageJson": true
      }
    }
  }
}

rspack.config.ts

import { composePlugins, withNx } from '@nx/rspack';

export default composePlugins(withNx());

tsconfig.app.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "commonjs",
    "types": [
      "node"
    ],
    "emitDecoratorMetadata": true,
    "target": "es2021"
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "src/**/*.spec.ts",
    "src/**/*.test.ts"
  ]
}

Points:

  1. I had to add "outputHashing": "none", because of now it is outputing main.[hash].js (not like in @nx/rspack 20.1.*)
  2. TS compiler option "emitDecoratorMetadata": true is added, but it is losing decorators and the injection does not work.
  3. "@nx/rspack": "20.1.4" - works fine

Part of code:

// ...
import { ConfigService } from '@nestjs/config';

@Injectable()
export class OpenIdService implements OnModuleInit, OnModuleDestroy {
  protected readonly logger = new Logger(OpenIdService.name);

  constructor(configService: ConfigService<EnvironmentVariables>) {
    this.logger.log('[OpenIdService constructor:configService]', configService);
    // ...
  }
  // ...
}

Error:

[Nest] 99352  - 01/10/2025, 2:05:21 PM     LOG [NestFactory] Starting Nest application...
[Nest] 99352  - 01/10/2025, 2:05:21 PM     LOG [OpenIdService] [OpenIdService constructor:configService]
[Nest] 99352  - 01/10/2025, 2:05:21 PM     LOG [OpenIdService] undefined
[Nest] 99352  - 01/10/2025, 2:05:21 PM     LOG [OpenIdService] [OpenIdService constructor:configService]
[Nest] 99352  - 01/10/2025, 2:05:21 PM     LOG [OpenIdService] undefined
[Nest] 99352  - 01/10/2025, 2:05:21 PM   ERROR [ExceptionHandler] Cannot read properties of undefined (reading 'get')

TypeError: Cannot read properties of undefined (reading 'get')
    at new OpenIdService (.../build/apps/auth/webpack:/auth/src/open-id/open-id.service.ts:50:49)
    at Injector.instantiateClass (.../node_modules/@nestjs/core/injector/injector.js:373:19)
    at callback (.../node_modules/@nestjs/core/injector/injector.js:65:45)
    at Injector.resolveConstructorParams (.../node_modules/@nestjs/core/injector/injector.js:145:24)
    at Injector.loadInstance (.../node_modules/@nestjs/core/injector/injector.js:70:13)
    at Injector.loadProvider (.../node_modules/@nestjs/core/injector/injector.js:98:9)
    at .../node_modules/@nestjs/core/injector/instance-loader.js:56:13
    at async Promise.all (index 3)
    at InstanceLoader.createInstancesOfProviders (.../node_modules/@nestjs/core/injector/instance-loader.js:55:9)
    at.../node_modules/@nestjs/core/injector/instance-loader.js:40:13

@zvn2060
Copy link

zvn2060 commented Jan 15, 2025

Same problem, with rspack.

It seems due to the module.rules process the typescript wrongly

// Rspack's docs only suggest swc for TS compilation
//https://rspack.dev/guide/tech/typescript
{
test: /\.([jt])sx?$/,
loader: 'builtin:swc-loader',
exclude: /node_modules/,
type: 'javascript/auto',
options: {
jsc: {
parser: {
syntax: 'typescript',
decorators: true,
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
pragma: 'React.createElement',
pragmaFrag: 'React.Fragment',
throwIfNamespace: true,
// Config.mode is already set based on options.mode and `process.env.NODE_ENV`
development: config.mode === 'development',
refresh: config.mode === 'development',
useBuiltins: false,
},
},
},
},
},

according to the rspack official nestjs example, the module rules should be:

https://github.com/rspack-contrib/rspack-examples/blob/2d5a7442832e7354e11503aa704dee2535ec7a09/rspack/nestjs/rspack.config.js#L14-L35

so, when I add a workaround plugin in my rspack.config.js

const { NxAppRspackPlugin } = require('@nx/rspack/app-plugin');
const rspack = require('@rspack/core');
const { join } = require('path');

module.exports = {
  entry: "src/main.ts",
  output: {
    path: join(__dirname, '../../dist/apps/example'),
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: {
          loader: 'builtin:swc-loader',
          options: {
            jsc: {
              parser: {
                syntax: 'typescript',
                decorators: true,
              },
              transform: {
                legacyDecorator: true,
                decoratorMetadata: true,
              },
            },
          },
        },
      },
    ],
  },
  optimization: {
    minimizer: [
      new rspack.SwcJsMinimizerRspackPlugin({
        minimizerOptions: {
          compress: {
            keep_classnames: true,
            keep_fnames: true,
          },
          mangle: {
            keep_classnames: true,
            keep_fnames: true,
          },
        },
      }),
    ],
  },
  plugins: [
    new NxAppRspackPlugin({
      target: 'node',
      tsConfig: 'apps/example/tsconfig.app.json',
      main: 'apps/example/src/main.ts',
      outputHashing: 'none',
    }),
    {
      apply(compiler) {
        compiler.options.module.rules = compiler.options.module.rules.slice(0, 1)
        compiler.options.optimization ??= {}
      }
    }
  ],
};

The NestJS work correctly.

@eXist-FraGGer
Copy link

@zvn2060 Thanks, but still have issues, now is another one and looks like the issue is already created:
#29514

@ICEDLEE337
Copy link

same issue w/ NestJS and @nx/rspack after running npx nx g @nx/rspack:configuration to add to an existing NestJS app.

@AgentEnder AgentEnder added the scope: node Issues related to Node, Express, NestJS support for Nx label Jan 22, 2025
@eXist-FraGGer
Copy link

eXist-FraGGer commented Feb 20, 2025

@zvn2060 I just investigated what is generating withNx and found the small difference.

So I have created a small plugin that fixes the config module rules.

@ndcunningham I hope it will help you to understand the problem and add this to a next fix/release.

The original rule:

{
        "test": {},
        "loader": "builtin:swc-loader",
        "exclude": {},
        "type": "javascript/auto",
        "options": {
            "jsc": {
                "parser": {
                    "syntax": "typescript",
                    "decorators": true,
                    "tsx": true
                },
                "transform": {
                    "react": {
                        "runtime": "automatic",
                        "pragma": "React.createElement",
                        "pragmaFrag": "React.Fragment",
                        "throwIfNamespace": true,
                        "development": false,
                        "refresh": false,
                        "useBuiltins": false
                    }
                }
            }
        }
    }

As we can see there are no rule.options.jsc.transform.legacyDecorator and rule.options.jsc.transform.decoratorMetadata.

Solution to create a plugin for fixing rule:

export const fixBuiltinSwcLoader = (config: Configuration): Configuration => {
  config.module.rules = config.module.rules.map((rule) => {
    if (typeof rule === 'object' && 'loader' in rule && rule.loader === 'builtin:swc-loader') {
      (rule.options as any).jsc.transform.legacyDecorator = true;
      (rule.options as any).jsc.transform.decoratorMetadata = true;
    }

    return rule;
  });

  return config;
};

rspack.config.ts

import { composePlugins, withNx } from '@nx/rspack';

export default composePlugins(
  withNx(),
  fixBuiltinSwcLoader,
);

In this case it still works together with NX and target executor @nx/rspack:rspack, everything can be easily configured with replacements like {workspaceRoot}, {projectRoot} and etc.

Additionally I have noticed that externals were also broken, so need also to have a plugin:

export const withNodeExternals = ({ fileName }: { fileName: string }) => (config: Configuration): Configuration => {
  config.externals = [
    nodeExternals({
      modulesFromFile: {
        fileName,
      },
    }),
  ];

  return config;
};

The final rspack.config.ts

import * as path from 'node:path';
import { composePlugins, withNx } from '@nx/rspack';
import { fixBuiltinSwcLoader, withNodeExternals } from '@shared';

export default composePlugins(
  withNx(),
  fixBuiltinSwcLoader,
  withNodeExternals({
    fileName: path.resolve(__dirname, 'package.json'),
  }),
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope: node Issues related to Node, Express, NestJS support for Nx type: bug
Projects
None yet
Development

No branches or pull requests

6 participants