Cree una API GraphQL con Guice y Spark

GraphQL es un lenguaje de consulta moderno para recuperar datos de un servidor. Existe una gran cantidad de documentación sobre la creación de API para un montón de plataformas, pero desafortunadamente la documentación oficial de Java contiene solo un ejemplo, que implica la creación de una aplicación basada en Spring Framework . El ejemplo oculta algunos detalles de implementación, lo que obliga al usuario a leer la fuente. En este artículo, arreglaremos esto y crearemos un análogo en la combinación de Google Guice y Spark . Antes de continuar, le recomiendo que se familiarice con el tutorial original , porque No profundizaré en la arquitectura de la biblioteca y la descripción de las entidades Java GraphQL





1. Creando una aplicación Guice

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"
      }
    }
  }
}
      
      



Código fuente completo e información adicional




All Articles