Erstellen Sie eine GraphQL-API mit Guice und Spark

GraphQL ist eine moderne Abfragesprache zum Abrufen von Daten von einem Server. Es gibt eine große Menge an Dokumentation zum Erstellen von APIs für eine ganze Reihe von Plattformen, aber leider enthält die offizielle Java-Dokumentation nur ein Beispiel, bei dem eine Anwendung basierend auf dem Spring Framework erstellt wird . Das Beispiel verbirgt einige Implementierungsdetails und zwingt den Benutzer, die Quelle zu lesen. In diesem Artikel werden wir dies beheben und ein Analogon zur Kombination von Google Guice und Spark erstellen . Bevor Sie fortfahren, empfehle ich Ihnen, sich mit dem Original- Tutorial vertraut zu machen , weil Ich werde nicht auf die Architektur der Bibliothek und die Beschreibung der Java GraphQL- Entitäten eingehen





1. Erstellen einer Guice-Anwendung

Gradle. , gradle init







:





Select type of project to generate: application
Select implementation language: Java
Select build script DSL: Kotlin
Select test framework: JUnit Jupiter
Project name: guice-spark-graphql
Source package: guice.spark.graphql
      
      



Java src/main/java/guice/spark/graphql/App.java







build.gradle.kts







dependencies {
    // This dependency is used by the application.
    implementation("com.google.guava:guava:29.0-jre")
    //Guice
    implementation("com.google.inject:guice:5.0.1") //NEW

    //Spark
    implementation("com.sparkjava:spark-core:2.9.3") //NEW
    implementation("com.sparkjava:spark-template-velocity:2.7.1") ////NEW spark template engine
    implementation("org.slf4j:slf4j-simple:1.7.21") //NEW fix Spark SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
    implementation("com.fasterxml.jackson.core:jackson-databind:2.12.3") //NEW

    //graphql
    implementation("com.graphql-java:graphql-java:16.2") //NEW

    // Use JUnit Jupiter API for testing.
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.2")

    // Use JUnit Jupiter Engine for testing.
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.2")
}

      
      



Guice, GraphQL Spark Slf4j, Jackson Velocity Template. Guava , -





2. GraphQL

GraphQL , API. schema.graphqls



src/main/resources







type Query {
  bookById(id: ID): Book 
}

type Book {
  id: ID
  name: String
  pageCount: Int
  author: Author
}

type Author {
  id: ID
  firstName: String
  lastName: String
}
      
      



3. Guice

Guice DI src/main/java/guice/spark/graphql/App.java







public class App {
    @Inject
    private GraphQLService service;

    public static void main(String[] args) {
        App app = new App();

        Injector injector = Guice.createInjector(new GraphQLModule());
        injector.injectMembers(app);

        app.service.initialize();
    }
}

      
      



Guice Injector, GraphQLModule . injector.injectMembers(app)



service GraphQLService



, ,





4. GraphQLModule

Guice . src/main/java/guice/spark/graphql/GraphQLModule.java







public class GraphQLModule extends AbstractModule {
    protected void configure() {
        bind(GraphQLService.class).asEagerSingleton();
        bind(GraphQL.class).toProvider(GraphQlProvider.class).asEagerSingleton();
        bind(ObjectMapper.class).asEagerSingleton();
    }
}
      
      



configure



:





  • GraphQLService



    Spark c Spark, GraphQL





  • GraphQL







  • ObjectMapper



    JSON





5. GraphQlProvider

, GraphQL



, Provider



Guice, . GraphQlProvider







public class GraphQlProvider implements Provider<GraphQL> {

    private GraphQLDataFetchers graphQLDataFetchers;

    @Inject
    public GraphQlProvider(GraphQLDataFetchers graphQLDataFetchers) {
        this.graphQLDataFetchers = graphQLDataFetchers;
    }

    @Override
    public GraphQL get() {
        URL url = Resources.getResource("schema.graphqls");
        String sdl = null;
        try {
            sdl = Resources.toString(url, Charsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        GraphQLSchema graphQLSchema = buildSchema(sdl);
        return GraphQL.newGraphQL(graphQLSchema).build(); 
    }

    private GraphQLSchema buildSchema(String sdl) {
        TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }

    private RuntimeWiring buildWiring() {
        return RuntimeWiring.newRuntimeWiring()
                .type(newTypeWiring("Query")
                        .dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
                .type(newTypeWiring("Book")
                        .dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
                .build();
    }
}

      
      



get() , GraphQL . ,





6. GraphQLDataFetchers

DataFetcher



GraphQL Java. DataFetcher - .





src/main/java/guice/spark/graphql/GraphQLDataFetchers.java







@Singleton
public class GraphQLDataFetchers {
    private static List<Map<String, String>> books = Arrays.asList(
            ImmutableMap.of("id", "book-1",
                    "name", "Harry Potter and the Philosopher's Stone",
                    "pageCount", "223",
                    "authorId", "author-1"),
            ImmutableMap.of("id", "book-2",
                    "name", "Moby Dick",
                    "pageCount", "635",
                    "authorId", "author-2"),
            ImmutableMap.of("id", "book-3",
                    "name", "Interview with the vampire",
                    "pageCount", "371",
                    "authorId", "author-3")
    );

    private static List<Map<String, String>> authors = Arrays.asList(
            ImmutableMap.of("id", "author-1",
                    "firstName", "Joanne",
                    "lastName", "Rowling"),
            ImmutableMap.of("id", "author-2",
                    "firstName", "Herman",
                    "lastName", "Melville"),
            ImmutableMap.of("id", "author-3",
                    "firstName", "Anne",
                    "lastName", "Rice")
    );

    public DataFetcher getBookByIdDataFetcher() {
        return dataFetchingEnvironment -> {
            String bookId = dataFetchingEnvironment.getArgument("id");
            return books
                    .stream()
                    .filter(book -> book.get("id").equals(bookId))
                    .findFirst()
                    .orElse(null);
        };
    }

    public DataFetcher getAuthorDataFetcher() {
        return dataFetchingEnvironment -> {
            Map<String, String> book = dataFetchingEnvironment.getSource();
            String authorId = book.get("authorId");
            return authors
                    .stream()
                    .filter(author -> author.get("id").equals(authorId))
                    .findFirst()
                    .orElse(null);
        };
    }
}
      
      



@Singleton



Guice - . configure



GraphQLModule



, -





7. GraphQLService

API web . GraphQLService







src/main/java/guice/spark/graphql/GraphQLService.java







public class GraphQLService {
    private final GraphQL graphQL;
    private final ObjectMapper mapper;

    @Inject
    public GraphQLService(GraphQL graphQL, ObjectMapper mapper) {
        this.graphQL = graphQL;
        this.mapper = mapper;
    }

    public void initialize() {
        post("/graphql", (request, response) -> {
            GraphQLRequestBody body = mapper.readValue(request.body(), GraphQLRequestBody.class);

            String query = body.getQuery();
            if (query == null) {
                query = "";
            }

            ExecutionResult executionResult = graphQL.execute(
                    ExecutionInput.newExecutionInput()
                            .query(query)
                            .operationName(body.getOperationName())
                            .variables(body.getVariables())
                            .build()
            );

            response.type("application/json");
            return mapper.writeValueAsString(executionResult.toSpecification());
        });
        
        get("/playground", (req, res) -> new VelocityTemplateEngine().render(
                new ModelAndView(Collections.emptyMap(), "playground.html"))
        );
    }

      
      



initialize



Jetty web server 4567. :





POST http://localhost:4567/graphql ObjectMapper



GraphQLRequestBody



GraphQL . JSON .





GraphQLRequestBody:







public class GraphQLRequestBody {
    private String query;
    private String operationName;
    private Map<String, Object> variables;

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public String getOperationName() {
        return operationName;
    }

    public void setOperationName(String operationName) {
        this.operationName = operationName;
    }

    public Map<String, Object> getVariables() {
        return variables;
    }

    public void setVariables(Map<String, Object> variables) {
        this.variables = variables;
    }
}
      
      



GET http://localhost:4567/playground playground.html , src/main/resources



. API .





. http://localhost:4567/playground





API

. :





query {
  bookById(id: "book-1") {
    name,
    author {
      firstName
    }
  }
}

      
      



API:





{
  "data": {
    "bookById": {
      "name": "Harry Potter and the Philosopher's Stone",
      "author": {
        "firstName": "Joanne"
      }
    }
  }
}
      
      



Vollständiger Quellcode und zusätzliche Informationen




All Articles