NestJS + GraphQL: How to avoid circular dependencies in resolvers

NestJS + GraphQL: How to avoid circular dependencies in resolvers


2

Let’s say i have two entities:

// user.entity.ts

@ObjectType()
@Entity()
export class User {
  @Field()
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Field()
  @Column({ unique: true })
  username: string;

  @Column({ select: false })
  password: string;

  @Field(() => [Task])
  @OneToMany(() => Task, (task) => task.author)
  authorOfTasks: Task[]
}

and

// task.entity.ts

@ObjectType()
@Entity()
export class Task {
  @Field()
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Field()
  @Column()
  title: string;

  @Field()
  @Column({ nullable: true })
  description?: string;

  @Field(() => User)
  @ManyToOne(() => User, (user) => user.authorOfTasks)
  author: User;
}

So in resolvers i need to resolve authorOfTasks field in UserResolver and author field in TaskResolver. To do this i need to inject UserService and TaskService in both UserResolver and TaskResolver. Like this:

// user.resolver.ts

@Resolver(of => User)  
export class UserResolver {  
  constructor(  
    private userService: UserService,  
    private taskService: TaskService  
  ) {}  

    // here are some queries that using userService
  
  @ResolveField(() => [Task])  
  async authorOfTasks(  
    @Parent() author: User  
  ) {  
    return this.taskService.getTasksByAuthor(author)  
  }  
}

and

// task.resolver.ts

@Resolver()  
export class TaskResolver {  
  constructor(  
    private taskService: TaskService,  
    private userService: UserService  
  ) {}  
    
  // here are some queries that using taskService  
  
  @ResolveField(() => User)  
  async author(  
    @Parent() task: Task  
  ) {  
    return this.userService.getUserById(task.author.id)  
  }  
}

As you can see there are TaskService and UserService injected in both those resolvers and it ends up with Nest complaining about circular dependencies in my app and suggesting me to use forwardRef to inject those dependencies. Ofcourse i can use that technique, but Nest documentation itself tells me that i should avoid circular dependencies when possible.

So my question is – what to do in this situation? Maybe i should reorganize my app somehow? How then? I want to keep my code modular as much as i can. My current structure looks like this:

NestJS + GraphQL: How to avoid circular dependencies in resolvers

Thank you! 🙏

4

  • Circular references are quite common in GraphQL and perfectly fine – a parent object refers to an array of children and the child refers to the parent(s). This is a nest detail.

    – Michel Floyd

    Jul 7 at 22:30

  • 1

    So basically i just need to omit Nest's suggestion of avoiding circular deps and go for forwardRef method?

    – Max Green

    Jul 8 at 9:37

  • Doesn't this circular dependency crash your tests? I receive the following error ` Cannot find module '@/user/entities' from 'tasks/entities/tasks.entity.ts'` when importing import { User } from '@/user/entities'; and having the following property: @ManyToOne(() => User, (user) => user.casinoTokens) user: User;

    – Vitomir

    Jul 11 at 9:47

  • Vitomir, i have not set up tests yet. It seems for me that this is path resolving problem, that is not related to my question. Probably you need to configure paths in tsconfig.json and/or jest.config.ts

    – Max Green

    Jul 12 at 11:10

1 Answer
1


0

While I do agree with @michel ‘s answer that circular dependencies are fairly common in GraphQL, I strongly disagree with the notion that one can "disable NestJS" warning for it.

The proper way for it would be to use forwardRef() as mentioned in NestJS doc here. I too had a circular dependency problem when using NestJS and GraphQL and I resolved it at the Module level instead of Service level.



Leave a Reply

Your email address will not be published. Required fields are marked *