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

Circular dependency issue when using functional syntax #1449

Open
zacharyclaysmith opened this issue Apr 27, 2023 · 7 comments
Open

Circular dependency issue when using functional syntax #1449

zacharyclaysmith opened this issue Apr 27, 2023 · 7 comments
Labels
Community 👨‍👧 Something initiated by a community Need More Info 🤷‍♂️ Further information is requested Question ❔ Not future request, proposal or bug issue

Comments

@zacharyclaysmith
Copy link

zacharyclaysmith commented Apr 27, 2023

Describe the Bug
A ReferenceError is thrown at startup due to circular references. These circular references occur while using the @Field(type => SomeType) syntax.

To Reproduce
Example Repository: https://github.com/zacharyclaysmith/type-graphql-circular-dependency-repro

(Key code extracts in Additional Context below)

Expected Behavior
The "functional syntax" should keep this kind of issue from happening:

From https://typegraphql.com/docs/0.17.1/types-and-fields.html:

Why use function syntax and not a simple { type: Rate } config object? Because, by using function syntax we solve the problem of circular dependencies (e.g. Post <--> User), so it was adopted as a convention. You can use the shorthand syntax @field(() => Rate) if you want to save some keystrokes but it might be less readable for others.

Logs
If applicable, add some console logs to help explain your problem.
You can paste the errors with stack trace that were printed when the error occurred.

Environment (please complete the following information):

  • OS: OSX
  • Node version: v18.5.0
  • type-graphql version: 2.0.0-beta.1
  • TypeScript version: 4.9.5
    • Additionally, using "type": "module" in package.json

Additional Context
This example was recreated from a larger project where I'm running into this issue. I tried to remove any unnecessary cruft when copying over pieces of code/dependency lists.

The original project also uses mikro-orm, and I avoid circular dependencies with their library due to the ability to use a string version of the type name, e.g. @Property('User').

Key Code extracts:

@ObjectType()
export class User {
  //...
	
  // NOTE: comment out the next line to remove the error.
  @Field(() => [Item])
  items: Item[];
}
@ObjectType()
export class Item {
  //...
	
  @Field(() => User)
  owner: User;
}
@zacharyclaysmith zacharyclaysmith changed the title Circular dependency issues on minimal code. Circular dependency issue when using functional syntax Apr 27, 2023
@carlocorradini
Copy link
Contributor

carlocorradini commented Apr 27, 2023

type: module is not currently supported in V2 beta until PR #1400 is merged.
I think this is the main cause of the issue 🙃

@carlocorradini
Copy link
Contributor

Does it work if you remove type: module?

@zacharyclaysmith
Copy link
Author

Tried it in this branch: https://github.com/zacharyclaysmith/type-graphql-circular-dependency-repro/tree/remove-type-module

It caused some cascading issues that had to be addressed (changing to commonjs and rewriting the index.ts file to not have a top-level await), but I think it technically works. I'll try similar in my actual application, though it's likely to not be as easy to solve with other major libraries in play.

@zacharyclaysmith
Copy link
Author

Now getting "TypeError: Class extends value undefined is not a constructor or null" in my main application after trying to make those changes (I'm using interfaces there that I didn't include in my original example). I'll see if I can recreate the issue in the example repo.

@MichalLytek MichalLytek added Question ❔ Not future request, proposal or bug issue Community 👨‍👧 Something initiated by a community Need More Info 🤷‍♂️ Further information is requested labels May 20, 2023
@LordotU
Copy link

LordotU commented Jun 24, 2024

Hello all,

I have the same issue with my setup in 2.0.0-rc2. Unfortunately, all the things in my monorepo are modules and it's very undesirable to change.

Is there any workaround which allows to stay on module?

@bkostrowiecki
Copy link

bkostrowiecki commented Nov 4, 2024

This issue prevents me from using TypeGraphQL with NextJS. NextJS tries to do its magic with optimization and it breaks with ReferenceError: Cannot access 'UserCreateNestedOneWithoutTicketsInput' before initialization due to the cyclic reference in Field decorators.

@gherciu
Copy link

gherciu commented Nov 24, 2024

Maybe I'm late to the party but that's how I fixed it:

I have somewhere in my app a function registerDto
this method registers all Dto in tsyringe container e.g

import { container } from 'tsyringe'
import { Country } from './resolvers/country/country.dto'
import { City } from './resolvers/city/city.dto'

// List of all Dto that may have circular deps, or depend on another dto
const circularDto = [Country, City]

export const registerCircularDto = () => {
  circularDto.forEach((DtoClass) => {
    container.register(`${DtoClass.name}Dto`, {
      // Using Value instead of use class, anyway we need to resolve just to what we provided
      // And when we resolve we do not want to initialize anything
      useValue: DtoClass,
    })
  })
}

Now in my file where I need a reference of that dto that have circular dependency with another I do this

city.ts

import type { CityEntity } from '@db/entities/city.entity'
import type { CountryEntity } from '@db/entities/country.entity'
import { container } from 'tsyringe'
import type { Country } from '../country/country.dto' // IMPORTANT: import as type not simple import

@ObjectType()
export class City implements CityEntity {

  @Field(() => container.resolve<Country>('CountryDto'), {
    nullable: true,
  })
  country: CountryEntity

}

country.ts

import type { CountryEntity } from '@db/entities/country.entity'
import type { CityEntity } from '@db/entities/city.entity'
import { container } from 'tsyringe'
import type { City } from '../city/city.dto' // IMPORTANT: import as type since these do not evaluate as circular in TS

@ObjectType()
export class Country implements CountryEntity {

  @Field(() => [container.resolve<City>('CityDto')], {
    nullable: true,
  })
  cities: CityEntity[]

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community 👨‍👧 Something initiated by a community Need More Info 🤷‍♂️ Further information is requested Question ❔ Not future request, proposal or bug issue
Projects
None yet
Development

No branches or pull requests

7 participants
@LordotU @zacharyclaysmith @bkostrowiecki @MichalLytek @carlocorradini @gherciu and others