Bangun API GraphQL dengan Guice dan Spark

GraphQL adalah bahasa kueri modern untuk mengambil data dari server. Ada banyak sekali dokumentasi tentang membangun API untuk sejumlah besar platform, tetapi sayangnya dokumentasi resmi Java hanya berisi satu contoh, yang melibatkan pembuatan aplikasi berdasarkan Spring Framework . Contoh tersebut menyembunyikan beberapa detail implementasi, memaksa pengguna untuk membaca sumbernya. Pada artikel ini, kami akan memperbaikinya dan membuat analog pada kombinasi Google Guice dan Spark . Sebelum melanjutkan, saya sarankan Anda membiasakan diri dengan tutorial asli , karena Saya tidak akan mempelajari arsitektur perpustakaan dan deskripsi entitas Java GraphQL





1. Membuat aplikasi 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"
      }
    }
  }
}
      
      



Kode sumber lengkap dan informasi tambahan




All Articles