nest: request property in a service class is undefined after v9.3.1

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

I have a simple constrictor for a service class as following

constructor( @Inject(REQUEST) public readonly request: Request, runAuthDependincies: Boolean = true, )

after you had released v9.3.1 this request is undefined

Minimum reproduction code

https://codesandbox.io/s/withered-monad-zgg1nu

Steps to reproduce

just updating the packages to 9.3.1

Expected behavior

The request property should not be undefined as before

Package

Other package

No response

NestJS version

9.3.1

Packages versions

"@hapi/hapi": "^21.0.0",
        "@hapi/joi": "^17.1.1",
        "@nestjs-modules/mailer": "^1.8.1",
        "@nestjs/common": "^9.0.0",
        "@nestjs/config": "^2.2.0",
        "@nestjs/core": "^9.0.0",
        "@nestjs/jwt": "^9.0.0",
        "@nestjs/passport": "^9.0.0",
        "@nestjs/platform-express": "^9.0.0",
        "apollo-server-express": "^3.11.1",
        "apollo-server-plugin-base": "^3.7.1",
        "class-transformer": "^0.5.1",
        "class-validator": "^0.13.2",
        "dotenv": "^16.0.3",
        "googleapis": "^109.0.1",
        "nodemailer": "^6.9.0",
        "passport-google-oauth20": "^2.0.0",
        "reflect-metadata": "^0.1.13",
        "rimraf": "^3.0.2",
        "rxjs": "^7.2.0"

Node.js version

16.3

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 4
  • Comments: 62 (45 by maintainers)

Most upvoted comments

Right, will look at it as well as soon as I get some free time. Disappointed my first contribution broke stuff…

Having the same issue:

  • The request is undefined when injecting (using @Inject(REQUEST)) in a service which is marked as scope.REQUEST
  • After the 9.3.1 update this problem suddenly occurred
  • After reverting the Nest packages back to 9.2.1 the problem dissappears
  • I cant get it reproduced in CodeSandbox, which suggests to me another dependency in my module is causing this problem. I am not using durability in my project (nor in the sandbox)

https://codesandbox.io/p/sandbox/runtime-star-dpm9rv?file=%2Fsrc%2Fapp.module.ts&selection=[{"endColumn"%3A1%2C"endLineNumber"%3A11%2C"startColumn"%3A1%2C"startLineNumber"%3A11}]

  • I simplified the code of @karam-mustafa without gateway 502 error
  • Removed noise or unused packages irrelevant to the problem
  • I updated and pinned all the packages to the latest versions (that I have locally as well)

Any tips on how I can help you refine this issue further from this point?

Can we revert #10698 and then revert #11018 ?

#11018 reverted. Now - not sure what we should do about this one #10698 - sounds like this issue only occurs when AutoMapper is included in the project?

@kamilmysliwiec I will try and get automapper in my repro repo, and see if I can figure out what the issue is.

Any pipes with scope REQUEST doesnt work 👍🏻

So I think I got to the bottom of it, and the PR #11041 has been updated. I’ve also added a diagram to explain the problem.

nope more investigation needed…

and I think I got a test reproducing the issue

describe.only('when wrapper is non static and dep is static', () => {
          it('should return false', () => {
            const wrapper = new InstanceWrapper({ scope: Scope.REQUEST });
            wrapper.addCtorMetadata(0, new InstanceWrapper());
            expect(wrapper.isDependencyTreeDurable()).to.be.false;
          });
        });

this fails. It should return false.

@kamilmysliwiec @micalevisk

I think I found the possible problem, at least in our code base it seems to be with pipes as they are marked as isTreeStatic = true. that obviously will return true when calling isDependencyTreeStatic() in instance-wrapper: isDependencyTreeDurable -> introspectDepsAttribute()

Adding {scope: Scope.REQUEST} to the Pipe makes it work.

our controller:

    @Get('/ourRoute')
    async ourRoute(
        @Request() req,
        @Query('additionalData', AdditionalDataPipe) additionalData?: AdditionalDataEnum[]
    ): Promise<GetCurrentUserResponse> {

The fixed Pipe:

@Injectable({
    scope: Scope.REQUEST,
})
export class AdditionalDataPipe implements PipeTransform<string, AdditionalDataEnum[]> {

So, not sure if I need to guard against Pipes and treat them differently, I think I need to dig deeper.

@kamilmysliwiec @micalevisk just experienced the problem in our code base with nestjs 9.3.2 and we do not use AutoMapper.

mm that should be ok as the first !isTreeNonDurable should return false so the second should not even be considered

But I’m still confused as I’ve now got a small app with a controller and a few different providers with different scopes (singleton, request, transient) and I cannot reproduce the issue…

I’ve done some really extensive digging/hacking (on my project, no NestJS internals) and removed almost everything from except for my printer controller and service. After that I went much further to delete almost everything that I could possibly delete or nullify from the dependencies. Still left with the error.

[server-nest] Info      03/02/2023, 00:01:50 [InstanceWrapper] PrinterController introspected as request-scoped - {} +0ms
[server-nest] Info      03/02/2023, 00:01:50 Starting server at port undefined - {} +3ms
[server-nest] Info      03/02/2023, 00:01:50 [InstanceWrapper] PrinterController introspected as durable - {} +5ms

So far what I think is left is a TypeORM feature called Printer (entity), AutoMapper and the two-layer mixin extensions I use to provide the controller with CRUD operations.

  • Controller => CrudController => ReadonlyCrudController
  • CrudService => ReadonlyCrudService
  • AutoMapper
  • TypeORM feature
  • HttpModule

With a fresh mind I should be able to port this into CodeSandbox. Cant promise it tho. Hope this helps.

@Durairaj that’s another one. Upgrade your @nestjs/core to 9.3

@micalevisk so I’m guessing this might be related to this PR https://github.com/nestjs/nest/pull/10809 (perhaps isDependencyTreeDurable returns true even though the tree isn’t durable for some edge-case scenarios?

ok got more understanding of the issue, I started commenting on the PR itself.

possible fix in the PR above

@vizio360 exactly. 9.3.2 doesn’t fix the bug introduced in 9.3.0

Correct, and we are not using ContextIdStrategy, so it sounds like the same issue @davidzwa is having.

@karam-mustafa are you also using AutoMapper?

I didn’t install it before

Can we revert https://github.com/nestjs/nest/pull/10698 and then revert https://github.com/nestjs/nest/pull/11018 ?

https://github.com/nestjs/nest/pull/11018 reverted. Now - not sure what we should do about this one https://github.com/nestjs/nest/pull/10698 - sounds like this issue only occurs when AutoMapper is included in the project?

what does that pesky automapper do??? 😃

Removing AutoMapper has removed the problem, but I must be fair: I’ve no idea why AutoMapper would be a durable provider or a reason for causing the static check to fail.

@davidzwa to clarify you are not setting up Durable Providers with a ContextidStrategy right?

Indeed, none of my services (or anything) follow the durable provider pattern described by https://docs.nestjs.com/fundamentals/injection-scopes#durable-providers

All is purely transient, singleton or request scoped.

What is interesting is that from the log @davidzwa provided the PrinterController does get request scope initially (see first line of the log) and then it gets durable…

[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterController introspected as request-scoped - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 Starting server at port 4000 - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [PrinterGateway] Gateway initialized - {} +1ms
[server-nest] Info      02/02/2023, 17:06:17 [WebSocketsController] PrinterGateway subscribed to the "init-request" message - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [AdminGateway] SocketIO AdminUI enabled. This can be disabled with: 'SOCKETIO_ADMINUI_ENABLED=false' - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterController introspected as durable - {} +5ms
[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterFileController introspected as durable - {} +0ms

@davidzwa could you check the dependencies chain of PrinterFileController and PrinterController and list them out? What I’m interested in is the scopes of each injected dependency and their dependencies.

Hopefully you don’t have too many 😃

Problem’s gone with your suggestion. (FYI: my post above was 9.3.1 ofcourse)

[server-nest] Info      02/02/2023, 17:58:08 [InstanceWrapper] PrinterFileController introspected as request-scoped - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in SentryCoreModule - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in TypeOrmCoreModule - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in AutomapperModule - {} +0ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Found automapper:nestjs:default in AutomapperModule - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in CacheModule - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in SafeEventEmitterModule - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [InjectorLogger] Looking for automapper:nestjs:default in OidcModule - {} +0ms
[server-nest] Info      02/02/2023, 17:58:08 [InstanceWrapper] PrinterController introspected as request-scoped - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 Starting server at port 4000 - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [PrinterGateway] Gateway initialized - {} +0ms
[server-nest] Info      02/02/2023, 17:58:08 [WebSocketsController] PrinterGateway subscribed to the "init-request" message - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [AdminGateway] SocketIO AdminUI enabled. This can be disabled with: 'SOCKETIO_ADMINUI_ENABLED=false' - {} +1ms
[server-nest] Info      02/02/2023, 17:58:08 [UserModule] Admin user found - skipping automatic creation step - {} +11ms
[server-nest] Info      02/02/2023, 17:58:08 [SettingsService] Cached entity with key settings.1 and id 1 - {} +19ms

No more 'introspected as durable for the PrinterFileController nor PrinterController.

That tip is really nice! I think this extract might help you:

[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterController introspected as request-scoped - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 Starting server at port 4000 - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [PrinterGateway] Gateway initialized - {} +1ms
[server-nest] Info      02/02/2023, 17:06:17 [WebSocketsController] PrinterGateway subscribed to the "init-request" message - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [AdminGateway] SocketIO AdminUI enabled. This can be disabled with: 'SOCKETIO_ADMINUI_ENABLED=false' - {} +0ms
[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterController introspected as durable - {} +5ms
[server-nest] Info      02/02/2023, 17:06:17 [InstanceWrapper] PrinterFileController introspected as durable - {} +0ms

Before the listen call we see PrinterController introspected as request-scoped, immediately after the listen(...) call the logs show PrinterController introspected as durable. This is the function that prints that (just making sure that I follow what is happening): https://github.com/nestjs/nest/blob/a439cd9a4120526a95295691e1ebbbce95d64f90/packages/core/injector/instance-wrapper.ts#L255

@davidzwa another thing you could do: define the env var NEST_DEBUG=true and see if you got occurrences of ‘durable’ in your logs

@micalevisk in the 9.2.1 edition neither MiddlewareModule::bindHandler or MiddlewareModule::registerHandler function is being called (no breakpoints or console logs that I added seem to trigger in the transpiled js code of nestjs). These are the places your changes seem to reside. I might try 9.3.1 soon for comparison.

Im not in the position to share the repo. Will keep you posted if I have more info to share.

hi @davidzwa is there any chance to share your code in a private repository with me?

or maybe you can see what was done at PR #10809 (which is pretty small) and try to debug it locally yourself 😃

I’ll say it one more time.

Please provide a minimum reproduction repository (Git repository/StackBlitz/CodeSandbox project).

why reproductions are required

Otherwise, please use our Discord channel (Support). We are using GitHub to track Bug Reports, Feature Requests, and Regressions.

https://codesandbox.io/s/withered-monad-zgg1nu

I’ll say it one more time.

Please provide a minimum reproduction repository (Git repository/StackBlitz/CodeSandbox project).

why reproductions are required

Otherwise, please use our Discord channel (Support). We are using GitHub to track Bug Reports, Feature Requests, and Regressions.

  • are you using durable providers?
  • did you have some ContextIdStrategy? show us

here is my basic service class

import {
  google,
  Auth,
  GoogleApis,
  admin_directory_v1,
  drive_v3,
  drive_v2,
} from 'googleapis';

import { Request } from 'express';
import { Inject, Injectable, Scope } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';

@Injectable({ scope: Scope.REQUEST })

export class BaseGoogleServices {
  requestProp: Request;

  constructor(
    @Inject(REQUEST) public readonly request: Request,
    runAuthDependincies: Boolean = true,
  ) {
       this.requestProp = request;
    }

    if (runAuthDependincies) {
      this.resolveAuthDependencies();
    }
  }

  resolveAuthDependencies() {
    // do some code
  }
}