#Architecture Graphql Query: Handling Circular Dependencies in NestJS GraphQL

1 messages · Page 1 of 1 (latest)

radiant adder
#

Hello NestJS GraphQL Community,

I hope this message finds you well. I am currently working on a NestJS GraphQL project and have encountered a circular dependency challenge that I am seeking advice on.

In my application, I have two GraphQL Object Types: CollectionType and UserType.

CollectionType:
“‘
@ObjectType("Collection")
export class CollectionType extends PickType(BaseType, ['_id', 'createdAt', 'updatedAt'] as const) {
@Field()
title: string;

@Field()
description: string;

@Field(() => UserType) // Circular Dependency
user: typeof UserType;

}
“‘

UserType:
@ObjectType("User")
export class UserType {
@Field()
_id: string;

@Field()
email: string;

@Field()
role: string;

@Field(() => [CollectionType], { nullable: "items" }) // Circular Dependency
collections: CollectionType[];

@Field()
createdAt: string;

@Field()
updatedAt: string;

}

As you can see, there's a circular dependency between CollectionType and UserType, where CollectionType references UserType, and vice versa.

I'm curious to know how the community handles such circular dependencies effectively. Are there best practices, patterns, or specific architectural approaches within NestJS GraphQL that can help resolve or manage circular dependencies like these?

Any guidance or insights into this matter would be greatly appreciated. Thank you in advance for your help!

Best regards,
Fahad.

spice laurelBOT
#

Please format your question or answer with Markdown formatting.
It leads to better readability and an easier time to spot problems.
For code blocks, you can wrap your block with three back ticks before and after the block, and after the first three back ticks you can add a language (like ts) to add syntax highlighting.
e.g.

```ts
@Injectable()
export class MySuperAwesomeService {
constructor(@Inject('InjectionToken') private readonly dep: SomeDependency) {}

getRandomNumber(): number {
return Math.round(Math.random() * 1000);
}
}
```

Becomes :point_down:

@Injectable()
export class MySuperAwesomeService {
  constructor(@Inject('InjectionToken') private readonly dep: SomeDependency) {}

  getRandomNumber(): number {
    return Math.round(Math.random() * 1000);
  }
}
stone orbit
#

I usually only put scalar fields to my DTOs (classes decorated with @ObjectType). For the relations I register new field in a resolver of that class. Given your example, I'd do this:

@ObjectType("Collection")
export class CollectionType extends PickType(BaseType, ['_id', 'createdAt', 'updatedAt'] as const) {
    @Field()
    title: string;

    @Field()
    description: string;
}

@ObjectType("User")
export class UserType {
    @Field()
    _id: string;

    @Field()
    email: string;

    @Field()
    role: string;

    @Field()
    createdAt: string;

    @Field()
    updatedAt: string;
}

@Resolver(() => CollectionType)
export class CollectionResolver {
   @ResolveField(() => UserType)
   user(@Parent() parent: CollectionType): UserType {
       return this.userService.getForCollection(parent.id); // for illustration only
   }
}


@Resolver(() => UserType)
export class UserResolver {
   @ResolveField(() => [CollectionType])
   collections(@Parent() parent: UserType): CollectionType[] {
       return this.userService.getCollections(parent.id); // for illustration only
   }
}

The resulting shape of both CollectionType and UserType in the schema will be the same, since nest combines the definitions, and the circular dependency is gone, since object types don't depend on each other.

radiant adder
stone orbit
#

The vice versa is a bit problematic. Often the app can be structured so that there are no cyclic dependencies. If you have a specific example of methods from both services that require the other service, I can try suggesting how that could be avoided.

For completeness, it is possible to have 2 services depending on each other using forwardRef (there's a topic on that in nest docs), but I find it a code smell usually.

radiant adder
radiant adder
stone orbit
#

I find something like this working for me:
Resolvers: inject services, perform auth using guards, maybe do some slight input transformation, call & return results from services
Services: perform business logic, validations, call repositories, maybe transform data to fit the Resolver shape. Often inject multiple repositories.
Repositories: just query the database and perform inserts/updates using db library of choice

But there are many existing posts in here on how to structure your app and code. GraphQL resolvers are not too different from usual Controllers, the same patterns often apply, so you might want to look around in other posts.