Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GraphQL and Spring Boot 2.0.3

I want to test GraphQL for a future project. The project will use Spring Boot, Spring Security, and GraphQL. So I created a new Spring Boot App in IntelliJ using the build in Spring Initializr. The Spring Boot version is, of course, the latest (2.0.3.RELEASE) Now I add the dependencies for GraphQL and GraphiQL.

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>5.2.0</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>

To test this, I ran the application but there was no /graphql endpoint. In this case, I configured it in application.properties without a problem:

# GraphQL
graphql.servlet.mapping=/graphql
graphql.servlet.enabled=true
graphql.servlet.corsEnabled=true
# GraphiQL
graphiql.mapping=/graphiql
graphiql.endpoint=/graphql
graphiql.enabled=true
graphiql.cdn.enabled=true
graphiql.cdn.version=0.11.11

After testing again, the endpoint is there, so now I could write a schema, resolvers and so on. This is what I implemented:

Schema:

schema {
    query: Query
    mutation: Mutation
}

type Greeting {
    id: ID!
    message: String!
}

type Query {
    greetingsAll: [Greeting]
}

type Mutation {
    greeting(message: String!): Greeting
}

Model:

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
@ToString
@Entity
public class Greeting {
    @Id
    @GeneratedValue
    private Long id;
    private String message;
}

Repository

@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> {
}

QueryResolver

@Component
public class QueryResolver implements GraphQLQueryResolver {

    @Autowired
    private GreetingRepository greetingRepository;

    public Greeting greeting(Long id) {
        return greetingRepository.getOne(id);
    }

    public Iterable<Greeting> getGreetingsAll() {
        return greetingRepository.findAll();
    }
}

MutationResolver

@Component
public class MutationResolver implements GraphQLMutationResolver {

    @Autowired
    private GreetingRepository greetingRepository;

    public Greeting newGreeting(String message) {
        Greeting greeting = new Greeting();
        greeting.setMessage(message);

        return greetingRepository.save(greeting);
    }
}

Beans for SpringBoot

@SpringBootApplication
public class GraphqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(GraphqlApplication.class, args);
    }

    @Bean
    ApplicationRunner init(GreetingRepository greetingRepository) {
        return args -> {
            Stream.of("Hallo", "Guten Tag", "Moin").forEach(greeting -> greetingRepository.save(Greeting.builder().message(greeting).build()));
            greetingRepository.findAll().forEach(System.out::println);
        };
    }

    @Bean
    public QueryResolver query() {
        return new QueryResolver();
    }

    @Bean
    public MutationResolver mutation() {
        return new MutationResolver();
    }
}

While trying to test the application again to see if I could run a query using GraphiQL, the application wouldn't start:

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:155) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:544) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at de.eno.prototyp.graphql.GraphqlApplication.main(GraphqlApplication.java:14) [classes/:na]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:126) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:86) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:413) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:174) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:179) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:152) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
    ... 8 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServletRegistrationBean' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServletRegistrationBean' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServlet' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServlet' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256) 
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLServlet' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLServlet' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    ... 24 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchemaProvider' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLWebAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchemaProvider' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    ... 38 common frames omitted
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'graphQLSchema' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Unsatisfied dependency expressed through method 'graphQLSchema' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:474) 
    ... 52 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schemaParser' defined in class path resource [com/oembedler/moon/graphql/boot/GraphQLJavaToolsAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:590) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1256) 
    ... 66 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.coxautodev.graphql.tools.SchemaParser]: Factory method 'schemaParser' threw exception; nested exception is com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:582) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
    ... 79 common frames omitted
Caused by: com.coxautodev.graphql.tools.FieldResolverError: Found more than one matching resolver for field 'FieldDefinition{name='greetingsAll', type=ListType{type=TypeName{name='Greeting'}}, inputValueDefinitions=[], directives=[]}': [MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}, MethodFieldResolver{method=public java.lang.Iterable de.eno.prototyp.graphql.QueryResolver.getGreetingsAll()}]
    at com.coxautodev.graphql.tools.FieldResolverScanner.findFieldResolver(FieldResolverScanner.kt:39) ~[graphql-java-tools-5.2.0.jar:na]
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanResolverInfoForPotentialMatches(SchemaClassScanner.kt:227) ~[graphql-java-tools-5.2.0.jar:na]
    at com.coxautodev.graphql.tools.SchemaClassScanner.handleRootType(SchemaClassScanner.kt:122) ~[graphql-java-tools-5.2.0.jar:na]
    at com.coxautodev.graphql.tools.SchemaClassScanner.scanForClasses(SchemaClassScanner.kt:80) ~[graphql-java-tools-5.2.0.jar:na]
    at com.coxautodev.graphql.tools.SchemaParserBuilder.scan(SchemaParserBuilder.kt:150) ~[graphql-java-tools-5.2.0.jar:na]
    at com.coxautodev.graphql.tools.SchemaParserBuilder.build(SchemaParserBuilder.kt:156) ~[graphql-java-tools-5.2.0.jar:na]
    at com.oembedler.moon.graphql.boot.GraphQLJavaToolsAutoConfiguration.schemaParser(GraphQLJavaToolsAutoConfiguration.java:65) ~[graphql-spring-boot-autoconfigure-4.3.0.jar:na]

So I don't know what happened but I think GraphQL can not map the Query type to the QueryResolver. But why?

When checking the internet, I read that Spring Boot Version is the reason. So I tested it with Spring Boot 1.5.8.RELEASE, without result.

However, when using an older GraphQL version (4.3.0 and 5.2.0) with Spring boot 1.5.8, it did seem to work fine. So then I tried to use these older GraphQL versions with the new Spring boot version, but then I get another exception:

2018-07-13 14:45:57.666  INFO 12872 --- [io-8080-exec-10] graphql.servlet.GraphQLServlet           : Bad POST request: parsing failed

java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
    at org.apache.catalina.connector.Request.parseParts(Request.java:2826) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
    at org.apache.catalina.connector.Request.getParts(Request.java:2793) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
    at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1084) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
    at graphql.servlet.GraphQLServlet.lambda$new$2(GraphQLServlet.java:129) ~[graphql-java-servlet-5.1.0.jar:na]
    at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:260) ~[graphql-java-servlet-5.1.0.jar:na]
    at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:278) ~[graphql-java-servlet-5.1.0.jar:na]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.31.jar:8.5.31]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.31.jar:8.5.31]

Now I'm really screwed up because of this incompatibility. I want to use the new versions of all libraries. Does anybody have a solution?

like image 542
Jonas Wolff Avatar asked Jul 13 '18 12:07

Jonas Wolff


People also ask

Can we use GraphQL with spring boot?

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short time. Combined with the GraphQL Java Tools library, we need only write the code necessary for our service.

Does spring support GraphQL?

Server Transports. Spring for GraphQL supports server handling of GraphQL requests over HTTP, WebSocket, and RSocket.

What is@ Spring Boot application?

Spring Boot helps developers create applications that just run. Specifically, it lets you create standalone applications that run on their own, without relying on an external web server, by embedding a web server such as Tomcat or Netty into your app during the initialization process.

What is GraphQL kickstart?

Provide libraries for using GraphQL Java. Some of these libraries used to be part of GraphQL Java itself, but have been extracted.


1 Answers

You're creating your resolver beans twice. Both your MutationResolver and QueryResolver are annotated with @Component, which means that Spring will create an instance and register it. However, within the GraphqlApplication you also created beans for both resolvers using the @Bean annotation.

This causes an issue, because the GraphQL library will look for a proper resolver for greetingsAll, but it will find two resolvers due to your bean being mapped twice.

The solution is to either remove the @Component annotation, or to remove the following bean configuration:

@Bean
public QueryResolver query() {
    return new QueryResolver();
}

@Bean
public MutationResolver mutation() {
    return new MutationResolver();
}
like image 86
g00glen00b Avatar answered Sep 19 '22 14:09

g00glen00b