/graphql-java

GraphQL Java implementation

Primary LanguageJavaMIT LicenseMIT

graphql-java

Join the chat at https://gitter.im/graphql-java/graphql-java

Friendly warning: As GraphQL itself is currently a Working Draft, expect changes.

This is a GraphQL Java implementation based on the specification and the JavaScript reference implementation.

Status: Version 2.3.0 is released.

The versioning follows Semantic Versioning since 2.0.0.

Hint: This README documents the latest release, but master contains the current development version. So please make sure to checkout the appropriate tag when looking for the version documented here.

Build Status Latest Release Latest Dev Build

Table of Contents

Overview

This is a Java Implementation of GraphQL. The library aims for real-life usage in production.

It takes care of parsing and executing a GraphQL query. It doesn't take care of actually fetching any data: Data comes from implementing callbacks or providing static data.

Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By contributing to this project (commenting or opening PR/Issues etc) you are agreeing to follow this conduct, so please take the time to read it.

Discussion

If you have a question or want to discuss anything else related to this project:

Hello World

This is the famous "hello world" in graphql-java:

import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import java.util.Map;

import static graphql.Scalars.GraphQLString;
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
import static graphql.schema.GraphQLObjectType.newObject;

public class HelloWorld {

    public static void main(String[] args) {
        GraphQLObjectType queryType = newObject()
                .name("helloWorldQuery")
                .field(newFieldDefinition()
                        .type(GraphQLString)
                        .name("hello")
                        .staticValue("world"))
                .build();

        GraphQLSchema schema = GraphQLSchema.newSchema()
                .query(queryType)
                .build();

        GraphQL graphQL = GraphQL.newGraphQL(schema).build();

        Map<String, Object> result = graphQL.execute("{hello}").getData();
        System.out.println(result);
        // Prints: {hello=world}
    }
}

Getting started

How to use the latest release with Gradle

Make sure mavenCentral is among your repos:

repositories {
    mavenCentral()
}

Dependency:

dependencies {
  compile 'com.graphql-java:graphql-java:2.3.0'
}
How to use the latest release with Maven

Dependency:

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>2.3.0</version>
</dependency>

Manual

Schema definition

Built-in Types

The Scalars Class has the following built-in types:

  • GraphQLString
  • GraphQLBoolean
  • GraphQLInt
  • GraphQLFloat
Creating a new Object Type

Example:

GraphQLObjectType simpsonCharacter = newObject()
    .name("SimpsonCharacter")
    .description("A Simpson character")
    .field(newFieldDefinition()
            .name("name")
            .description("The name of the character.")
            .type(GraphQLString))
    .field(newFieldDefinition()
            .name("mainCharacter")
            .description("One of the main Simpson characters?")
            .type(GraphQLBoolean))
.build();
Creating a new Interface Type

Example:

GraphQLInterfaceType comicCharacter = newInterface()
    .name("ComicCharacter")
    .description("A abstract comic character.")
    .field(newFieldDefinition()
            .name("name")
            .description("The name of the character.")
            .type(GraphQLString))
    .build();
Creating a new Union Type

Example: (a snippet from here)

GraphQLUnionType PetType = newUnionType()
    .name("Pet")
    .possibleType(CatType)
    .possibleType(DogType)
    .typeResolver(new TypeResolver() {
        @Override
        public GraphQLObjectType getType(Object object) {
            if (object instanceof Cat) {
                return CatType;
            }
            if (object instanceof Dog) {
                return DogType;
            }
            return null;
        }
    })
    .build();
    
Creating a new Enum Type

Example:

GraphQLEnumType colorEnum = newEnum()
    .name("Color")
    .description("Supported colors.")
    .value("RED")
    .value("GREEN")
    .value("BLUE")
    .build();
       
Creating a Object-Input Type

Example:

GraphQLInputObjectType inputObjectType = newInputObject()
        .name("inputObjectType")
        .field(newInputObjectField()
                .name("field")
                .type(GraphQLString))
        .build();
Lists and NonNull

GraphQLList and GraphQLNonNull wrap another type to declare a list or to forbid null values.

There are no builders to create new objects. Just normal constructors, because they are so simple.

Example:

new GraphQLList(GraphQLString); // a list of Strings

new GraphQLNonNull(GraphQLString); // a non null String
Creating a Schema

Example:

GraphQLSchema schema = GraphQLSchema.newSchema()
    .query(queryType) // must be provided
    .mutation(mutationType) // is optional
    .build();
            

A full schema example: StarWarsSchema

Another schema example, including union types: GarfieldSchema

Recursive Type References

GraphQL supports recursive Types: For example a Person can contain a list of friends of the same Type.

To be able to declare such a Type, graphql-java has a GraphQLTypeReference class.

When the schema is created, the GraphQLTypeReference is replaced with the actual real Type Object.

For example:

GraphQLObjectType person = newObject()
    .name("Person")
    .field(newFieldDefinition()
            .name("friends")
            .type(new GraphQLList(new GraphQLTypeReference("Person"))))
    .build();

Data fetching

The actual data comes from DataFetcher objects.

Every field definition has a DataFetcher. When no one is configured, a PropertyDataFetcher is used.

PropertyDataFetcher fetches data from Map and Java Beans. So when the field name matches the Map key or the property name of the source Object, no DataFetcher is needed.

Example of configuring a custom DataFetcher:

DataFetcher<Foo> fooDataFetcher = new DataFetcher<Foo>() {
    @Override
    public Foo get(DataFetchingEnvironment environment) {
        // environment.getSource() is the value of the surrounding
        // object. In this case described by objectType
        Foo value = perhapsFromDatabase(); // Perhaps getting from a DB or whatever 
        return value;
    }
};

GraphQLObjectType objectType = newObject()
        .name("ObjectType")
        .field(newFieldDefinition()
                .name("foo")
                .type(GraphQLString)
                .dataFetcher(fooDataFetcher))
        .build();

Executing

To execute a Query/Mutation against a Schema build a new GraphQL Object with the appropriate arguments and then call execute().

The result of a Query is a ExecutionResult Object with the result and/or a list of Errors.

Example: GraphQL Test

More complex examples: StarWars query tests

Execution strategies

All fields in a SelectionSet are executed serially per default.

You can however provide your own execution strategies, one to use while querying data and one to use when mutating data.

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        2, /* core pool size 2 thread */
        2, /* max pool size 2 thread */
        30, TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>(),
        new ThreadPoolExecutor.CallerRunsPolicy());

GraphQL graphQL = GraphQL.newObject(StarWarsSchema.starWarsSchema)
        .queryExecutionStrategy(new ExecutorServiceExecutionStrategy(threadPoolExecutor))
        .mutationExecutionStrategy(new SimpleExecutionStrategy())
        .build();

When provided fields will be executed parallel, except the first level of a mutation operation.

See specification for details.

Alternatively, schemas with nested lists may benefit from using a BatchedExecutionStrategy and creating batched DataFetchers with get() methods annotated @Batched.

JDK8 Lambdas

This project is built using JDK6. But if you're using JDK8 and above then you can also use lambdas.

GraphQLObjectType queryType = newObject()
                .name("helloWorldQuery")
                .field(field -> field.type(GraphQLString)
                        .name("hello")
                        .argument(argument -> argument.name("arg")
                                .type(GraphQLBoolean))
                        .dataFetcher(env -> "hello"))
                .build();

Logging

Logging is done with SLF4J. Please have a look at the Manual for details. The grapqhl-java root Logger name is graphql.

Relay Support

There is a very basic Relay support included. Please look at https://github.com/andimarek/todomvc-relay-java for an example project how to use it.

Relay sends queries to the GraphQL server as JSON containing a query field and a variables field. The query field is a JSON string, and the variables field is a map of variable definitions. A relay-compatible server will need to parse this JSON and pass the query string to this library as the query and the variables map as the third argument to execute as shown below. This is the implementation from the todomvc-relay-java example.

@RequestMapping(value = "/graphql", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Object executeOperation(@RequestBody Map body) {
    String query = (String) body.get("query");
    Map<String, Object> variables = (Map<String, Object>) body.get("variables");
    ExecutionResult executionResult = graphql.execute(query, (Object) null, variables);
    Map<String, Object> result = new LinkedHashMap<>();
    if (executionResult.getErrors().size() > 0) {
        result.put("errors", executionResult.getErrors());
        log.error("Errors: {}", executionResult.getErrors());
    }
    result.put("data", executionResult.getData());
    return result;
}

Contributions

Every contribution to make this project better is welcome: Thank you!

In order to make this a pleasant as possible for everybody involved, here are some tips:

Development Build

The latest development build is available on Bintray.

Please look at Latest Build for the latest version value.

Javadocs

See the project page for the javadocs associated with each release.

How to use the latest build with Gradle

Add the repositories:

repositories {
    mavenCentral()
    maven { url  "http://dl.bintray.com/andimarek/graphql-java" }
}

Dependency:

dependencies {
  compile 'com.graphql-java:graphql-java:INSERT_LATEST_VERSION_HERE'
}

How to use the latest build with Maven

Add the repository:

<repository>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
    <id>bintray-andimarek-graphql-java</id>
    <name>bintray</name>
    <url>http://dl.bintray.com/andimarek/graphql-java</url>
</repository>

Dependency:

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java</artifactId>
    <version>INSERT_LATEST_VERSION_HERE</version>
</dependency>

Build it

Just clone the repo and type

./gradlew build

In build/libs you will find the jar file.

Running the tests:

./gradlew test

Installing in the local Maven repository:

./gradlew install

Details

The implementation is in Java 6, but the tests are in Groovy and Spock.

The query parsing is done with ANTLR. The grammar is here.

The only runtime dependencies are ANTLR and Slf4J.

Acknowledgment

This implementation is based on the js reference implementation. For example the StarWarSchema and the tests (among a lot of other things) are simply adapted to the Java world.

Related projects

graphql-rxjava: An execution strategy that makes it easier to use rxjava's Observable

graphql-java-annotations: Annotations-based syntax for GraphQL schema definition.

graphql-java-servlet: Servlet that automatically exposes a schema dynamically built from GraphQL queries and mutations.

License

graphql-java is licensed under the MIT License. See LICENSE for details.

Copyright (c) 2015, Andreas Marek and Contributors

graphql-js License