Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping multiple graphQL schema files to separate resolvers - Spring Boot

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?

like image 438
DulleX Avatar asked Nov 28 '22 00:11

DulleX


1 Answers

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 :)

like image 196
DulleX Avatar answered Jan 13 '23 15:01

DulleX