Using 'this.' in a NestJS method decorator argument (e.g. @Mutation(() => this.signupService.Response), and questions about static methods

Viewed 286

I'm using NestJs in a monorepo setup using Nx, with NextJs as a client. My basic architecture is having modules that contain all the logic (client, server and api code) for a specific domain (auth, users, posts etc), and then having services that these modules consume (CssService, DbService, JwtService, ValidationService etc). The folder structure looks something like this (I've removed unnecessary barrel/Nx lib files for this demo):

modules
  auth
    api
      signup
        client
          service-methods-and-properties
            signup.error-messages.ts
            signup.fields.ts
            signup.form.logic.ts
            signup.form.ui.tsx
            signup.form.tsx
            signup.mutation.generated.ts
            signup.mutation.gql
          signup.service.ts
          signup.types.ts

        server
          service-methods-and-properties
            signup.input.ts
            signup.logic.ts
            signup.response.ts
          signup.service.ts
          signup.types.ts

    client
      pages
        signup
          signup.page.tsx

    server
      auth.module.ts
      auth.resolver.ts

services
  db
    client
      db.service
    server
      db.service

In my NestJs auth resolver (modules/auth/server/auth.resolver), I import the SignupServerService to be used by the signup handler:

import { SignupServerService } from '../api/signup/server';

@Resolver()
export class AuthResolver {
  constructor(private readonly signupServerService: SignupServerService) {}

  @Mutation(() => SignupServerService.Response)
  async signup(
    @Args('input') input: SignupServerService.Input
  ): Promise<typeof SignupServerService.Response.prototype> {
    return this.signupServerService.logic(input);
  }

}

Here is the SignupServerService:

@Injectable()
export class SignupServerService {
  constructor(
    private readonly gqlServerService: GqlServerService,
    protected readonly dbServerService: DbServerService,
    protected readonly jwtServerService: JwtServerService
  ) {}

  static readonly Input = SignupInput;

  static readonly Response = SignupResponse;

  readonly logic = signupLogic;
}

However, there are a couple of problems with this, and some related questions I have:

  1. The code works, but I get a couple of TS errors for this line in my resolver @Args('input') input: SignupServerService.Input, which disappears if I use SignupInput directly from the signup.input.ts file, rather than accessing it statically via the SignupServerService class. The errors are 'SignupServerService' only refers to a type, but is being used as a namespace here.ts(2702) and Parameter 'input' of public method from exported class has or is using private name 'SignupServerService'.ts(4073)

  2. Piggy backing on problem 1, I'm using some static properties on the SignupServerService so I can pass SignupServerService.Response to the @Mutation decorator args, because the following doesn't work @Mutation(() => this.signupServerService.Response). I'm wondering why this doesn't work? And is my pattern of using static methods to pass properties to the decorator args the best way of doing this? Should I be using TS namespaces instead of static class methods or is there a better way of handling this?

  3. Furthermore, I've heard that static methods cause issues with unit testing and should be avoided? This is mentioned in the logging section of the NestJs docs, but it isn't clear enough to draw any conclusions: "This technique, while simple, doesn't utilize dependency injection for the MyLogger class. This can pose some challenges, particularly for testing, and limit the reusability of MyLogger"

  4. This leads me to a more general question about using NestJs in a monorepo. Frequently I need to share a class/service (for example my custom JwtService - note, this is not NestJs's built in JwtService) between some NestJs code and some React code. Naturally, functional react components don't use a DI system, so to access properties on the JwtService I need to either make them static (e.g. JwtService.ACCESS_TOKEN_COOKIE_NAME) which means duplicating the property in the JwtService class (e.g. static ACCESS_TOKEN_COOKIE_NAME = ACCESS_TOKEN_COOKIE_NAME; ACCESS_TOKEN_COOKIE_NAME = ACCESS_TOKEN_COOKIE_NAME), or, don't use static methods and instantiate the service manually in my react code (e.g. const { ACCESS_TOKEN_COOKIE_NAME } = new JwtService()). However, this becomes unusable if the service has dependencies which I have to manually provide to the class constructor argument, particularly if the dependecies are only used by methods used in the NestJs code (e.g. const { ACCESS_TOKEN_COOKIE_NAME } = new JwtService(new SomeDependencyNotNeededForReact())). So what is the best solution in this scenario?

0 Answers
Related