Alex Turdean
Alex Turdean

Reputation: 69

My graphql resolvers have a circular dependency

I changed my repositories so that they return the GQL resolvers directly. It was all nice until I added a circular dependency: now I have 2 repositories that depend on each other. There is no way JavaScript can resolve this dependency. Is there anything I could do to fix this? The repositories look something like this:

ARepository.getA = () => {
   const a = getAFromDatabase();
   return {
      ...a,
      B: BRepository.getB()
}

And the second repository:

BRepository.getB = () => {
   const b = getBFromDatabase();
   return {
      ...b,
      A: ARepository.getA()
}

Each repository depends on the other, so there is no way to define 1 before the other. No repository can be defined.

Upvotes: 0

Views: 420

Answers (1)

Alex Turdean
Alex Turdean

Reputation: 69

That's an antipattern. You can create a generic resolver for a GraphQL type, but not to that degree.

From the start, the fact that you coupled the repository with the resolver is not good. The repository should be responsible for getting records from the database, and then you can have a resolver that calls repositories.

You should define a specific resolver for each query, not per type. If you have multiple queries that resolve the same types, you can abstract them into some functions (let's call them "gqlWrappers"). Those gqlWrappers should extend your database-raw-records with resolvers for types/fields that are leaf-nodes/simple-objects/value-objects. (if you don't want to get into circular dependency).

If you have a gqlWrapper for type A that depends on B's gqlWrapper, and then you extend B's gqlWrapper so that it depends on A's gqlWrapper, you end up in the same cyclical dependency that cannot be resolved.

Let's say that you have type A and type B, and both have a reference to each other, but also other parts that you could abstract. This is how I would implement the resolver for getB Query:

Queries: {
    getB: async () => {
       const b = BRepository.getB(); // The repo no longer returns complete resolvers, only database-raw-data
       return {
          ...BGqlWrapper(b),
          A: async () => {
              const a = ARepository.getA();
              return AGqlWrapper(a);  // I don't want to let the user get B again from A when he already started from B
          }
       }
    }
}

Upvotes: 1

Related Questions