This is the seventh day of my participation in the First Challenge 2022. For details: First Challenge 2022.
Integrate GraphQL in 3 minutes
GraphQL is a query language that examines data from the server. To some extent, it is an alternative to REST, SOAP, or gRPC.
Suppose we want to query the details of a particular book from the background of an online store.
Using GraphQL, you can send the following query to the server to get the details of the book with id “book-1”.
{
bookById(id: "book-1"){
id
name
pageCount
author {
firstName
lastName
}
}
}
Copy the code
This is not JSON, although it looks like it. It is a GraphQL query statement. The meaning of the statement is:
- Query a book with the specified ID
- Returns the id, name, pageCount, author of the book
- For the Author field, you also need to include firstName and lastName
The return result is a normal JSON format:
{
"bookById":
{
"id":"book-1"."name":"Harry Potter and the Philosopher's Stone"."pageCount":223."author": {
"firstName":"Joanne"."lastName":"Rowling"}}}Copy the code
One of the most important features of GraphQL is that it is statically typed: the server explicitly knows the type of each object you can query, and the client can get “schema” from the server. Schema describes which queries are possible and which fields can be retrieved. (Note: The Schema we use here refers to GraphQL Schema, which has nothing to do with other schemas such as JSON Schema.)
The Schema queried above is as follows:
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
Copy the code
This tutorial will focus on implementing a GraphQL service in Java using Schema.
GraphQL Java overview
GraphQL Java is the Java server implementation of GraphQL. There are several repositories at GraphQL Java Github org. The most important one is the GraphQL Java Engine(github.com/graphql-jav…
GraphQL Java Engine only cares about query execution. It doesn’t handle anything HTTP or JSON related. Based on these aspects, we will use the GraphQL Java SpringBoot adapter, which is responsible for exposing our API over HTTP via SpringBoot.
The main steps to create a GraphQL Java service are:
- Define GraphQL Schema
- Determine how a query retrieves data
Example API: Get book details
Our example project will write a simple interface to get the details of a given clerk. This is not a comprehensive API, but it is sufficient for this tutorial.
Create a SpringBoot project
Import 3 packages:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>17.3</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-spring-boot-starter-webmvc</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1 - jre</version>
</dependency>
Copy the code
The first two are GraphQL Java and GraphQL Java Spring. Guava is not necessary, but it makes it easier.
Schema
Create a new file schema. Graphqls under SRC /main/resources and say:
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
Copy the code
The Schema defines a top-level field (in the Query type) : bookById, which returns the specified memorized details.
It also defines the type Book, which contains fields: ID, Name,pageCount,author. The type of author is author, which is also defined after the Book type.
The specialized Language used above to describe schemas is called Schema Definition Language, or DSL. More details can be found at graphql.org/learn/schem…
When this file is complete, we have to use it.
Create a new class GraphQLProvider and define an init method that creates an instance of GraphQL:
@Component
public class GraphQLProvider {
private GraphQL graphQL;
@Bean
public GraphQL graphQL(a) {
return graphQL;
}
@PostConstruct
public void init(a) throws IOException {
URL url = Resources.getResource("schema.graphqls");
String sdl = Resources.toString(url, Charsets.UTF_8);
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
// TODO:We will create the Schema here later}}Copy the code
We use Guava’s Resources to read the files we created earlier. Next, create GraphQLSchema and GraphQL instances. This GraphQL instance is exposed as a Spring Bean through the GraphQL () method with the @Bean annotation. The GraphQL Java Spring adapter will use the GraphQL instance to validate our schema over HTTP, with the default URL: / GraphQL.
All we need to do is implement the buildSchema method, which creates the GraphQLSchema instance and connects the SDL to the DataFetcher.
@Autowired
GraphQLDataFetchers graphQLDataFetchers;
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(a) {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}
Copy the code
TypeDefinitionRegistry is a parsed version of a Schema file. SchemaGenerator combines TypeDefinitionRegistry and RuntimeWiring to generate GraphQLSchema.
The buildWiring method uses the graphQLDataFetchers bean to de-register 2 Datafetchers.
- One is to retrieve book by specifying an ID
- One is to get the author of the specified book
The DataFetcher and how to implement the GraphQLDataFetchers bean are explained in the next section.
createGraphQL
andGraphQLSchema
The overall process of the example is as follows:
DataFetchers
Perhaps the most important concept of GraphQL Java Server is the DataFetcher: at query execution time, a DataFetcher retrieves data for a field.
When GraphQL Java executes a query, it invokes the appropriate DataFetcher for each field in the query statement. A DataFetcher is an interface that has only one method and only one DataFetcherEnvironment parameter:
public interface DataFetcher<T> {
T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}
Copy the code
Important: Each field in the Schema has a DataFethcer associated with it. If you do not define a DataFetcher for a field, the default PropertyDataFetcher is used. We’ll talk about this later.
Let’s next create a new class GraphQLDataFetchers that contains the sample list of books and authors.
The complete implementation looks like this:
@Component
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(a) {
return dataFetchingEnvironment -> {
String bookId = dataFetchingEnvironment.getArgument("id");
return books
.stream()
.filter(book -> book.get("id").equals(bookId))
.findFirst()
.orElse(null);
};
}
public DataFetcher getAuthorDataFetcher(a) {
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); }; }}Copy the code
The data source
We get books and authors from the static list in this class, which will help you understand that GraphQL doesn’t require any data sources. This is GraphQL’s advantage: data can come from static data in memory, from a database, or from an external service.
Book DataFetcher
Our first method getBookByIdDataFetcher returns an implementation of a DataFetcher — using the DataFetcherEnvironment to return the specified book. What this example means is that we need to get the ID parameter from the bookById field and find the book that corresponds to that ID. If not found, null is returned.
String bookId = dataFetchingEnvironment.getArgument(“id”); The “id” in “bookById” refers to the query field in the schema:
type Query {
bookById(id: ID): Book
}
Copy the code
Author DataFetcher
The second method, getAuthorDataFetcher, returns an implementation of the DataFetcher — getting the author of the specified book. In contrast to book’s DataFetcher method, we don’t get arguments here, but we do have an instance of Book. The getSource method is used to get a valid superclass field and then the DataFetcher result. This is an important concept: The DataFetcher for each field in GraphQL is called in a top-down fashion, and the result of the parent class is the Source property of the subclass DataFetcherEnvironment.
We then use the previously obtained book to get the authorId, and then query the specified author in the same way we query the book.
Default DataFetchers
We only implemented 2 DataFetchers. As mentioned earlier, if you don’t implement it, then the default PropertyDataFetcher will be used. In our case, It means that the Book id, Book. The name, Book, pageCount, Author. Id, Author. Firstname, Author. LastName respectively has a default PropertyDataFetcher associated with it.
PropertyDataFetcher tries to get properties on Java objects in a number of ways. In the example, java.util.map makes it easy to find attributes by key. This is good for us because the keys of Maps for Book and Author are exactly the same as the fields in the schema. For example, in the schema, we define the field pageCount of type Book. The Map returned by the DataFetcher of Book has key pageCount. Because the field name is the same as the key in the Map, PropertyDateFetcher works.
Let’s assume that we have a field mismatch and the key in the book Map is totalPages, not pageCount. This will cause the pageCount value of all books to be null because the PropertyDataFetcher cannot get the correct value. To solve this problem, you need to register a new DataFetcher for book.pagecount, as shown below:
// In the GraphQLProvider class
private RuntimeWiring buildWiring(a) {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher())
// This line is new: we need to register the additional DataFetcher
.dataFetcher("pageCount", graphQLDataFetchers.getPageCountDataFetcher()))
.build();
}
// In the GraphQLDataFetchers class
// Implement the DataFetcher
public DataFetcher getPageCountDataFetcher(a) {
return dataFetchingEnvironment -> {
Map<String,String> book = dataFetchingEnvironment.getSource();
return book.get("totalPages");
};
}
Copy the code
Try out the API
That’s all you need to build the GraphQL API. After running SpringBoot project, using the http://localhost:8080/graphql address can see API.
The easiest way to try and see the GraphQL API is to use GraphQL Playground (www.graphql-java.com/tutorials/g…
Start after you will be asked to enter a URL, enter: http://localhost:8080/graphql
After that, you can query the sample API we wrote earlier.
graphql-java-kickstart
Graphql-java is the Java implementation of GraphQL. Graphql-java-spring-boot-starter-webmvc is designed to support SpringWeb style requests.
Graphql-java-kickstart is on top of that, including not only GraphQL-Java, but also graphQL-Java-tools and graphQL-Java-Servlet. With graphical interface support, we only need to introduce this dependency.
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>12.0.0</version>
</dependency>
Copy the code
Enable GraphIQL and PlayGround
Both of these are graphQL syntax for performing graphical interfaces.
The opening mode is as follows:
graphql:
graphiql:
enabled: true
playground:
enabled: true
Copy the code
Create schema
As before, you need to create a schema file, and the suffix must be.graphqls.
type Query {
bookById(id: String): Book
}
type Book {
id: String
name: String
pageCount: Int
author: Author
}
type Author {
id: String
firstName: String
lastName: String
}
Copy the code
Create the corresponding Object
Each data defined in Schema needs to have a corresponding Java object:
@Data
class Book{
private String id;
private String name;
private Integer pageCount;
private Author author;
}
@Data
class Author{
private String id;
private String firstName;
private String lastName;
}
Copy the code
Create a Resolver
Unlike before, this does not require us to write a detailed parsing step, just a Resolver.
@Component
public class BookResolver implements GraphQLQueryResolver {
public Book bookById(String id){
Book book = new Book();
book.setId("book1");
book.setName("test");
returnbook; }}Copy the code
The @Component annotation is meant to be found by Spring. The GraphQLQueryResolver interface is for parsing classes labeled as Schemas. The method name must be the same as the query name defined in the schema.
Start the SpringBoot
After starting SpringBoot, the browser accesses:http://localhost:8080/playground. It goes to the graphical interface
There are two buttons on the left, DOCS and SCHEMA. DOCS displays the query you defined and the details you can retrieve. SCHEMA will show you the SCHEMA you defined.
PostMan initiates the request
Localhost :8080/ graphQL POST {“query”:” graphQL statement “}
advantage
- The front end does not need multiple requests; one request can get the required data
- The front end decides what data it needs to fetch
- API files are up to date
The official page
Grammar: graphql. Cn/learn /
Source: github.com/graphql-jav…