A query language for your API
Recently, the company often uses this thing. I learned something out of interest, and recorded it as follows. If anything is wrong, I hope you can give me more advice.
I. Overview
GraphQL is a query language for api, a way to query existing data when the project is running. GraphQL provides a complete and understandable description of the data in the API, allows clients to ask for what they really need, makes the API easier to evolve over time, and supports powerful development tools.
APIJson can be compared at the same level. I personally feel that apiJson is more practical and simple in Java development. For example, permission verification can add role to table operation permissions through @ MethodAccess annotation Model, and support remote function calls. In contrast, graphql learning difficulty and cost And the standard is relatively high. But if you have too much skill, let's take a look today.
(APIjson personal feeling can refer to the blog, I feel pretty good https://blog.csdn.net/u014618954/article/details/107021638/)
Two preliminary display
Graphql is an api query language, which makes development more efficient and convenient to a certain extent. The following is an example.
For example, a GraphQL service users who log on to tell us (me) as well as the user's name might look like this:
① create
defined types and fields on these types and provides functions of each type for each field.
type Query {
me: User
}
type User {
id: ID
name: String
}
function Query_me(request) {
return request.auth.user;
}
function User_name(user) {
return user.getName();
}
②Query
{
me {
name
}
}
③Back
{
"me": {
"name": "Luke Skywalker"
}
}
The above is a query query, you can query all the names of the corresponding names according to the name, as well as the advantages of querying by type, multiple parameters, etc., not much to say, you can refer to the official document of graphql. The simple explanation is divided into three points, as follows.
Application scenarios :
①Field redundancy due to multiple platforms and other reasons
②Multiple calls to aggregate data on one page
③Frequent interface changes and simplified logic
Three practical applications
After talking about it for a long time, let’s get some dry goods. This time we demonstrate the basic use of Java and go.
3.1 java+spring boot+graphql
Our sample application will be a simple API to get the detailed information of a specific book.
① Build the directory
Select:
Gradle Project
Java
Spring Boot 2.1.x
Project metadata
Group: com.graphql-java.tutorial
Artifact: book-details
Dependency
web
② Add basic dependencies
com.graphql-java:graphql-java:11.0 // NEW
com.graphql-java:graphql-java-spring-boot-starter-webmvc:1.0 // NEW
com.google.guava:guava:26.0-jre // NEW
org.springframework.boot:spring-boot-starter-web
org.springframework.boot:spring-boot-starter-test
}
③ Create related files
Create schema.graphqls in the src/main/resources directory
//该部分是一个关于书籍的基本类型的定义,并与后续类型的查询有关
type Query {
bookById(id: ID): Book
}
type Book {
id: ID
name: String
pageCount: Int
author: Author
}
type Author {
id: ID
firstName: String
lastName: String
}
Create GraphQLProvider.class in the com.graphqljava.tutorial.bookdetails directory, and create a GraphQL instance:
@Component
public class GraphQLProvider {
private GraphQL graphQL;
@Bean
public GraphQL graphQL() {
return graphQL;
// 这个GraphQL实例通过使用@Bean注释的GraphQL()方法公开为一个Spring Bean
}
@PostConstruct
public void init() throws IOException {
//使用Guava资源从类路径读取文件
URL url = Resources.getResource("schema.graphqls");
//GraphQL Java Spring适配器将使用该GraphQL实例使我们的模式通过HTTP在默认url /
String sdl = Resources.toString(url, Charsets.UTF_8);
//创建一个GraphQLSchema和GraphQL实例
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
@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);
}
/*buildWiring使用graphQLDataFetchers bean实际注册两个DataFetchers:
一个用于检索具有特定ID的图书
一个用于获取特定书籍的作者*/
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(newTypeWiring("Query")
.dataFetcher("bookById", graphQLDataFetchers.getBookByIdDataFetcher()))
.type(newTypeWiring("Book")
.dataFetcher("author", graphQLDataFetchers.getAuthorDataFetcher()))
.build();
}
}
Important interface (do not create your own):
DataFetcher is an interface with a single method, it only accepts a single parameter of type DataFetcherEnvironment;
when GraphQL Java executes a query, it will call the appropriate for each field encountered in the query Data table device.
public interface DataFetcher<T> {
T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}
Let us assume that there is a mismatched book mapping that has a key totalPages instead of pageCount. This will result in an empty pageCount value for each book, because PropertyDataFetcher cannot get the correct value. In order to solve this problem, you must register a new DataFetcher for Book (not needed in this case)
// 在 GraphQLDataFetchers class
// 实现 DataFetcher
public DataFetcher getPageCountDataFetcher() {
return dataFetchingEnvironment -> {
Map<String,String> book = dataFetchingEnvironment.getSource();
return book.get("totalPages");
};
}
Create a new class GraphQLDataFetchers, which contains a list of examples of books and authors
@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() {
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);
};
}
}
Add the main method of application to start the project test
package com.graphqljava.tutorial.bookdetails;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BookDetailsApplication {
public static void main(String[] args) {
SpringApplication.run(BookDetailsApplication.class, args);
}
}
The results can be obtained
clearly through the web version test https://cpq-dev.ext.hp.com/playground
(the playground can install the corresponding test software, such as postman, altair, this article uses the web version, if any For questions, please refer to the source code: https://github.com/graphql-java/tutorials)
3.2 Golang + graphql
① Install corresponding support
go get github.com/graphql-go/graphql
(This directory is under the path directory. There was a problem with the guide package in the previous blog. You can browse it. If you don’t know the path directory of your project, you can use the go Current GOPATH command to find it)
② Application example
The following is a simple example, which defines a pattern with a hello string type field and a Resolve method that returns the string world. Execute GraphQL query on this mode, and print the result output in JSON format
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/graphql-go/graphql"
)
func main() {
// Schema
fields := graphql.Fields{
"hello": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return "world", nil
},
},
}
rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields}
schemaConfig := graphql.SchemaConfig{Query: graphql.NewObject(rootQuery)}
schema, err := graphql.NewSchema(schemaConfig)
if err != nil {
log.Fatalf("failed to create new schema, error: %v", err)
}
// Query
query := `
{
hello
}
`
params := graphql.Params{Schema: schema, RequestString: query}
r := graphql.Do(params)
if len(r.Errors) > 0 {
log.Fatalf("failed to execute graphql operation, errors: %+v", r.Errors)
}
rJSON, _ := json.Marshal(r)
fmt.Printf("%s \n", rJSON) // {"data":{"hello":"world"}}
}
The above is a simple example. For more interesting and complex case recommendations, please go to
https://github.com/graphql-go/graphql/tree/master/examples/
https://github.com/graphql-go/graphql/blob/master /graphql_test.go learn
I also have a preliminary study of graphql, and I also hope that the big guys can give more suggestions
This article refers to learning from
https://graphql.org/ official website and related git source code
Personal recommendation https://blog.csdn.net/qq_41882147/article/details/82966783, very detailed, the introduction is also more practical, suitable for advanced learning