GraphQL接口查询语言初步学习

A query language for your API

最近公司经常用到这东西,小编出于兴趣,学习一二,记录如下,如有不妥,还望多多指教。

一 概述

GraphQL是一种用于api的查询语言,在项目运行时对现有数据的一种查询方式。GraphQL为API中的数据提供了一个完整的、可理解的描述,允许客户端要求他们真正需要的东西,使API更容易随着时间的推移而发展,并支持强大的开发工具。

同级可对比APIJson,个人感觉apiJson在Java开发中比较实用和简单,如权限验证通过 @ MethodAccess 注解Model来添加角色对表的操作权限,以及支持远程函数调用,而相对比,graphql学习难度和成本以及规范性,都比较高。不过技多不压身,今天咱来看看。
(APIjson个人感觉可参考该博客,感觉还不错https://blog.csdn.net/u014618954/article/details/107021638/)

二 初步展示

graphql是api的查询语言,在一定程度上使开发更加高效和便捷,一下是一个例子。
例如,一个GraphQL服务告诉我们登录用户是谁(me)以及该用户的名字可能是这样的:
①创建
在这些类型上定义类型和字段,然后为每种类型的每个字段提供函数。

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();
}

②查询

{
  me {
    name
  }
}

③返回

{
  "me": {
    "name": "Luke Skywalker"
  }
}

上述为查询query,可根据name查询所有对应名字的name,以及通过type,多参等查询优点,具体也不多说,可参考graphql官方文档,简单讲解分为三点,如下。

应用场景
①多平台等原因导致的字段冗余
②一个页面多次调用聚合数据
③经常改动接口且精简逻辑

三 实际应用

讲了半天湿的,下面来点干货,本次演示Java和go两种语言的基本使用

3.1 java+spring boot+graphql

我们的示例应用程序将是一个简单的API,用于获取特定书籍的详细信息。

① 构建目录

Select:
	Gradle Project
	Java
	Spring Boot 2.1.x
Project metadata
	Group: com.graphql-java.tutorial
	Artifact: book-details
Dependency
	web

② 添加基础依赖


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
}

③ 创建相关文件
在src/main/resources目录下创建 schema.graphqls

//该部分是一个关于书籍的基本类型的定义,并与后续类型的查询有关
type Query {
  bookById(id: ID): Book 
}

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

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

在com.graphqljava.tutorial.bookdetails目录下创建GraphQLProvider.class,并创建一个GraphQL实例:

@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();
    }
}

重要的接口(不用自己创):
DataFetcher是一个带有单个方法的接口,它只接受DataFetcherEnvironment类型的单个参数;
当GraphQL Java执行查询时,它会为在查询中遇到的每个字段调用适当的数据表器。

public interface DataFetcher<T> {
    T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}

让我们假设有一个不匹配的图书映射有一个关键totalPages而不是pageCount。这将导致每本书的pageCount值为空,因为PropertyDataFetcher无法获取正确的值。为了解决这个问题,你必须为Book注册一个新的DataFetcher(本文案例不需要)

 // 在 GraphQLDataFetchers class
    // 实现 DataFetcher
    public DataFetcher getPageCountDataFetcher() {
        return dataFetchingEnvironment -> {
            Map<String,String> book = dataFetchingEnvironment.getSource();
            return book.get("totalPages");
        };
    }

创建一个新的类GraphQLDataFetchers,其中包含书籍和作者的示例列表

@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);
        };
    }
}

加入application的main方法启动项目测试

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);
    }

}

可通过网页版测试https://cpq-dev.ext.hp.com/playground
在这里插入图片描述
可以很清晰的拿到结果了
(该playground可自行安装相应测试软件,如postman,altair,本文使用网页版,如有问题可参考源码:https://github.com/graphql-java/tutorials)

3.2 Golang+graphql

① 安装相应支持

go get github.com/graphql-go/graphql

(此目录为path目录下,之前博客有讲导包问题,可前往浏览,如不知道自己项目path目录,可用 go Current GOPATH 命令查找)

② 应用举例
下面是一个简单的例子,它定义了一个带有hello字符串类型字段的模式和一个返回字符串世界的Resolve方法。对该模式执行GraphQL查询,结果输出以JSON格式打印

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

上述为简单示例,更多有趣复杂案例推荐可前往
https://github.com/graphql-go/graphql/tree/master/examples/
https://github.com/graphql-go/graphql/blob/master/graphql_test.go 学习

本人也是对graphql初步学习,也希望大佬多多提建议

本文参考学习于
https://graphql.org/官网及相关git源码

个人推荐https://blog.csdn.net/qq_41882147/article/details/82966783,挺详细,介绍也比较实际,可适合进阶学习

猜你喜欢

转载自blog.csdn.net/MatChen/article/details/111516086