Mapping multiple graphQL schema files to separate resolvers - Spring Boot
Asked Answered
T

3

6

I'm finding it really difficult to separate queries from one schema file. I want to have something like this:

car.graphqls

type Query {
    car(id: ID!): Car
}

type Car {
    id: ID!,
    name: String!
}

house.graphqls

type Query {
    house(id: ID!): House
}

type House {
    id: ID!,
    owner: String,
    street: String
}

I searched a lot but I can't find a way to write two java classes and implement getHouse() in one of them and getCar() in other.

@Component
public class CarQuery implements GraphQLQueryResolver {

    @Autowired
    private CarService carService;

    public List<Car> getCar(final int id) {
        return this.carService.getCar(id);
    }
}

public class HouseQuery implements GraphQLQueryResolver {

    @Autowired
    private HouseService houseService;

    public List<House> getHouse(final int id) {
        return this.houseService.getHouse(id);
    }
}

I found out that the graphql-java-tools package which I'm using will search through the project and finds all schema files (that end with .graphqls), but the code which I showed above gives me this error:

Caused by: com.coxautodev.graphql.tools.FieldResolverError: No method found with any of the following signatures (with or without one of [interface graphql.schema.DataFetchingEnvironment] as the last argument), in priority order:

  com.example.polls.resolvers.CarQuery.house(~count)
  com.example.polls.resolvers.CarQuery.getHouse(~count)

I also found some advises that I need to have only one Root Query in schema files, and to extend all other Query types in schema files. I tried to write to house.graphqls something like this, but failed:

extend Type Query {
    house(id: ID!): House
}

Is there a way to tell graphql and java what schema file I want to be mapped to which java resolver file?

Tough answered 22/2, 2020 at 21:59 Comment(7)
Does your project work with a single graphqls file? If not, you should make sure it does first.Lepley
Yes, it works if I put all queries in one schema file and if I keep only one GraphQLQueryResolver with all required methods. I find it really strange how little documentation and examples I could find related to this topic. All examples usually focus on one schema file. Can't find nothing on two schema files and I think it will be really useful to be able to split queries on multiple schema files. Projects can get really big and it would be not convenient to keep it all in one schema file.Tough
You said it works with one schema file and one big resolver, but does it work with one schema file and several small resolvers? That's how my graphql project is setup, so we should be able to make that work. From the logs, it looks like graphql-java-tools is parsing your graphqls file but cannot find your HouseQuery resolver.Lepley
@Tough Did you find the solution? I am stuck with similar issue.Paternalism
@Paternalism Unfortunately, No. I was just doing research on graphql with Spring and I gave up after couple of days, because I could not make it work with more schema files and more resolvers and I can't see reason to get more into it, because I think that, on larger projects, code maintenance will be impossible with one schema file and one resolver.Tough
@Lepley I think I was not able to make it work even with one schema and more resolvers. I would really appreciate if someone in the future who read this question can provide some example for multiple schema files or multiple resolvers. I am pretty sure it can be done, but can't figure how.Tough
@Tough I know for sure that multiple resolver files should work. In your case, it probably doesn't because you forgot the Component annotation on the HouseQuery class. Once you have this sorted, having multiple schema files should be straight forward. See this question/answer for guidance #56857188Lepley
L
11

Multiple schema files

Graphql-java-tools will find all the .graphqls files to build a schema instance. Multiple schema files work out of the box.

Multiple resolvers

Each resolver component must implement GraphQLQueryResolver or GraphQLMutationResolver and be scannable by spring boot. Make sure it has a @Component annotation and is in one of the spring @ComponentScan basePackages.

The error

No method found with any of the following signature means that graphql-tools was not able to find a resolver component with a method matching the signatures. In this case, the HouseQuery resolver is missing the @Component annotation.

Lepley answered 17/3, 2020 at 23:13 Comment(0)
T
17

Thanks AllirionX. Your answer was helpful.

I would just like to summarize final solution to all who are looking for answer how to create multiple schema files with separate query types in each of them and map those query types to different Java Components using GraphQLQueryResolver.

My Spring Boot project structure

I have two schema files A.graphqls and B.graphqls.

A.graphqls
---------------
type Person {
  id: ID!,
  name: String
}

type Query {
  getPerson(id: Int):Person
}

type Mutation {
  createPerson(name: String):Int
}

B.graphqls
---------------
type Book {
  id: ID!,
  title: String,
  owner: Person
}

extend type Query {
  getBooks(count: Int):[Book]
}

extend type Mutation {
  deleteBook(id: Int):Int
}

schema {
  query: Query,
  mutation: Mutation
}

I will explain what I learned about rules we need to follow about this topic (I don't guarantee that this is all necessary, but that is how I managed to get it work how I wanted it to work).

  1. The key here is to only have one schema definition. It doesn't matter in which file (A.graphqls or B.graphqls or C.graphqls...) - In example, I added it to B.graphqls file at the bottom.

  2. Also, you can have only one "type Query" definition in ONE file. In all other schema files you will need to extend that type with "extend type Query" (yeah, I know, it makes sense now...). In which schema file you do that main definition for Query that is not relevant. Everything in this paragraph applies to mutations also.

  3. You can use type defined in one .graphqls file in other .graphqls file. It will get recognized. So, in this example, you can use Person type reference in B.graphqls.

Java resolvers:

import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import graphql.demo.model.Person;
import graphql.demo.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class AQuery implements GraphQLQueryResolver {

  @Autowired
  private PersonService personService;

  public Person getPerson(final int id) {
      return this.personService.getPerson(id);
  }
}

And second one...

@Component
public class BQuery implements GraphQLQueryResolver {

  @Autowired
  private BookService bookService;

  public List<Book> getBooks(final int count) {
      return this.bookService.getBooks(count);
  }
}

Names of this classes are not important. We could also have only one class that implements GraphQLQueryResolver and we could implement all query methods from both A.graphqls and B.graphqls files (getBooks() and getPerson() methods). As long as we implement all methods, it's not important in which resolver class we implemented it graphql-java will find it. Same applies to mutations using GraphQLMutationResolver.

I have full working example (MySQL, Spring Boot, React with Apollo client) on my github, so you can check it out. There is also mysql script for generating database used in project. There is plenty of tables, but there are just for testing purposes, what is important is file structure and files I explained above. If you are not interested in client app, you can test it using graphiql, of course.

https://github.com/dusko-dime/spring-react-graphql

Hope this can be helpful to someone and thanks for helping me once again :)

Tough answered 29/3, 2020 at 17:3 Comment(0)
L
11

Multiple schema files

Graphql-java-tools will find all the .graphqls files to build a schema instance. Multiple schema files work out of the box.

Multiple resolvers

Each resolver component must implement GraphQLQueryResolver or GraphQLMutationResolver and be scannable by spring boot. Make sure it has a @Component annotation and is in one of the spring @ComponentScan basePackages.

The error

No method found with any of the following signature means that graphql-tools was not able to find a resolver component with a method matching the signatures. In this case, the HouseQuery resolver is missing the @Component annotation.

Lepley answered 17/3, 2020 at 23:13 Comment(0)
C
0

Also there can be issue with your Resolver name Please ensure that you specify the same name of your Resolver method as you have defined in your GraphQL Schema

Cheltenham answered 4/11, 2020 at 10:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.