GraphQL & Go,graphql基本知识,go-graphql使用

Posted 尚墨1111

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GraphQL & Go,graphql基本知识,go-graphql使用相关的知识,希望对你有一定的参考价值。

GraphQL & Go

1 RESTful

1.1 基本概念

概念:Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。

功能

资源:互联网所有的事物都可以被抽象为资源.每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作

1.2 HTTP VS REST

传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get

http://127.0.0.1/item/queryItem?id=1 查询,GET
http://127.0.0.1/item/addItem新增,POST
http://127.0.0.1/item/updateItem 更新,POST
http://127.0.0.1/item/deleteItem?id=1 删除,GET或POST

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果

http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE

举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees

下面是一些例子。

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

 URL组成 : 协议://IP地址:端口/路径?参数1=1&参数2=2... 
 传统带参访问方式:
     接口:/employees  
     访问路径 http://localhost:8080/employee?id=1&name=tom&age=23
     
 使用参数路径访问方式:
     接口:/employees/id/name/age   
     http://localhost:8080/employee/12/tom/23


2 GraphQL

学习中文官网


背景:REST接口无法满足变化的需求,一遍就得重新改

GraphQL 服务:每个类型的每个字段都由一个 resolver 函数支持,当一个字段被执行时,相应的 resolver 被调用以产生下一个值。如果字段产生标量值,例如字符串或数字,则执行完成。如果一个字段产生一个对象,则该查询将继续执行该对象对应字段的解析器,直到生成标量值

例如这个查询:


  me 
    name
  

会产生这样的JSON结果:


  "me": 
    "name": "Luke Skywalker"
  

2.1 Fields 字段:请求参数

如果类型是object,就可以无限套娃


  hero 
    name
    # 查询可以有备注!
    friends 
      name
    
  



  "data": 
    "hero": 
      "name": "R2-D2",
      "friends": [
        
          "name": "Luke Skywalker"
        ,
        
          "name": "Han Solo"
        ,
        
          "name": "Leia Organa"
        
      ]
    
  

2.2 参数

2.2.1 给字段传递参数的能力,定参

在类似 REST 的系统中,你只能传递一组简单参数 —— 请求中的 query 参数和 URL 段。但是在 GraphQL 中,每一个字段和嵌套对象都能有自己的一组参数,从而使得 GraphQL 可以完美替代多次 API 获取请求

GraphQL 自带一组默认标量类型:

  • Int:有符号 32 位整数。
  • Float:有符号双精度浮点值。
  • String:UTF‐8 字符序列。
  • Booleantrue 或者 false
  • ID:ID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型。

  human(id: "1000") 
    name
    height
  

2.2.2 变量:动态的参数

  1. 使用 $variableName 替代查询中的静态值。
  2. 声明 $variableName 为查询接受的变量之一。
  3. variableName: value 通过传输专用(通常是 JSON)的分离的变量字典中
#  "graphql": true, "variables":  "episode": JEDI  
query HeroNameAndFriends($episode: Episode) 
  hero(episode: $episode) 
    name
    friends 
      name
    
  

2.2.3 元字段

你并不知道你将从 GraphQL 服务获得什么类型,这时候你就需要一些方法在客户端来决定如何处理这些数据。GraphQL 允许你在查询的任何位置请求 __typename,一个元字段,以获得那个位置的对象类型名称


  search(text: "an") 
    __typename
    ... on Human 
      name
    



  "data": 
    "search": [
      
        "__typename": "Human",
        "name": "Han Solo"
      

2.3 请求类型

操作类型可以是 querymutationsubscription

query HeroNameAndFriends 
  hero 
    name
    friends 
      name
    
  

2.3.1 mutation:修改请求,类似post

# 定义
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) 
  createReview(episode: $ep, review: $review) 
    stars
    commentary
  


# 请求,相当于post

  "ep": "JEDI",
  "review": 
    "stars": 5,
    "commentary": "This is a great movie!"
  


# response

  "data": 
    "createReview": 
      "stars": 5,
      "commentary": "This is a great movie!"
    
  


3 go-graphql

GO文档:https://pkg.go.dev/github.com/graphql-go/graphql

schema:定义了对象中的哪些字段是我们期望得到的

resolve:解析器函数,每个特定的field被请求时触发执行这个函数


3.1 schema

3.1.1 graphql.Field

graphql的操作逻辑单位,名字、描述、接收参数、返回参数、解析器

type Field struct 
	Name              string              `json:"name"` // used by graphlql-relay
	Type              Output              `json:"type"`
	Args              FieldConfigArgument `json:"args"`
	Resolve           FieldResolveFn      `json:"-"`
	Subscribe         FieldResolveFn      `json:"-"`
	DeprecationReason string              `json:"deprecationReason"`
	Description       string              `json:"description"`


var DoQueryTasks = &graphql.Field
	Name:        "Tasks list page",
	Description: "query task list page",
	Type:        TaskListType,               //返回类型(列表)
	Args:        taskArgsNoTaskManageId,     //接收参数
	Resolve:     resolver.QueryTaskListPage, //处理解析器

3.1.2 输入参数定制

graphql.ArgumentConfig,接收的参数只有类型,没有处理逻辑,所以对应的是config类,不需要嵌套Field

// 参数配置
type ArgumentConfig struct 
	Type         Input       `json:"type"`
	DefaultValue interface `json:"defaultValue"`
	Description  string      `json:"description"`

// 参数定义
type FieldConfigArgument map[string]*ArgumentConfig

// 实现了入参的定义
graphql.FieldConfigArgument
	"id":                 &graphql.ArgumentConfigType: graphql.Int, Description: "任务ID",
	"name":  			 &graphql.ArgumentConfigType: graphql.String, Description: "姓名",

3.1.3 输出参数定制

graphql.NewObject,嵌套里面还会有嵌套,所以需要套娃式Fields

Object类型,用于描述层级或者树形数据结构。对于树形数据结构来说,叶子字段的类型都是标量数据类型。几乎所有 GraphQL 类型都是对象类型。Object 类型有一个 name 字段,以及一个很重要的 fields 字段。fields 字段可以描述出一个完整的数据结构。

func NewObject(config ObjectConfig) *Object

type ObjectConfig struct 
	Name        string      `json:"name"`
	Interfaces  interface `json:"interfaces"`
	Fields      interface `json:"fields"`
	IsTypeOf    IsTypeOfFn  `json:"isTypeOf"`
	Description string      `json:"description"`


// 定制输出结果
var TaskListType = graphql.NewObject(graphql.ObjectConfig
	Name: "TaskListPage",
	Fields: graphql.Fields
		"total": &graphql.FieldType: graphql.Int, Description: "总数",
		"list":  &graphql.FieldType: graphql.NewList(OutPutType), Description: "数据列表",
)

var OutPutType = graphql.NewObject(graphql.ObjectConfig
	Name: "OutPutType",
	Fields: graphql.Fields
		"id":           &graphql.FieldType: graphql.Int, Description: "ID",
		"createdAt":    &graphql.FieldType: scalars.CustomTime,Description: "创建时间",
		"createdBy":    &graphql.FieldType: graphql.String,Description: "创建人",
		"modifiedAt":   &graphql.FieldType: scalars.CustomTime,Description: "修改时间",
		"modifiedBy":   &graphql.FieldType: graphql.String,Description: "修改人",
	,
)

3.1.4 schema

graphql.SchemaConfig

type SchemaConfig struct 
	Query        *Object
	Mutation     *Object
	Subscription *Object
	Types        []Type
	Directives   []*Directive
	Extensions   []Extension

var Schema graphql.Schema

// init 初始化GraphQL
func init() 
	Schema, _ = graphql.NewSchema(graphql.SchemaConfig
		Query:    rootQuery,
		Mutation: rootMutation,
	)


// 查询操作graphql
var rootQuery = graphql.NewObject(
	graphql.ObjectConfig
		Name: "Query",
		Fields: graphql.Fields
			"tasks":               	schemas.DoQueryTasks,               //查询任务列表
			"users":                schemas.DoQueryUsers,                //查询用户列表
		,
		Description: "RootQuery",
	,
)
// 修改操作graphql
var rootMutation = graphql.NewObject(
	graphql.ObjectConfig
		Name: "Mutation",
		Fields: graphql.Fields
			"updateTask":              	 schemas.DoUpdateTask,             	    //更新任务
			"createTask":                schemas.DoCreateTask,                  //创建任务
			"deleteTask":                schemas.DoDeleteTask,                  //删除任务
		,
		Description: "RootMutation",
	,
)

3.2 解析器 ——处理函数

3.2.1 graphql.ResolveParams 透传的参数

// 请求结构
type ResolveParams struct 
	// Source is the source value
	Source interface

	// Args is a map of arguments for current GraphQL request
	Args map[string]interface

	// Info is a collection of information about the current execution state.
	Info ResolveInfo

	// Context argument is a context value that is provided to every resolve function within an execution.
	// It is commonly
	// used to represent an authenticated user, or request-specific caches.
	Context context.Context

3.2.2 Resolve

// 绑定参数并解析
func QueryListById(p graphql.ResolveParams) (interface, error) 
    // 定义参数结构体
	opts := entity.ListPageOptions
    // 解析前端传递参数
	jsonStr, _ := json.Marshal(p.Args)
	if err := json.Unmarshal(jsonStr, &opts); err != nil 
		return nil, err
	
	// 执行处理逻辑
	list, total, err := service.QueryListPage(&opts)
	if err != nil 
		return nil, err
	
    // 返回前端所需结构
	return dto.PageDataTotal: total, List: dto.GenListDTOs(list), nil


// 解析器会对应解析成输出的graphql结构
var ListType = graphql.NewObject(graphql.ObjectConfig
	Name: "ListPage",
	Fields: graphql.Fields
		"total": &graphql.FieldType: graphql.Int, Description: "总数",
		"list":  &graphql.FieldType: graphql.NewList(OutPutType), Description: "数据列表",
	)

3.3 路由控制

3.3.1 GraphqlHandler

graphql.Params、graphql.Do、graphql.Result

// 执行动作
func Do(p Params) *Result

// 执行结果
type Result struct 
	Data       interface                `json:"data"`
	Errors     []gqlerrors.FormattedError `json:"errors,omitempty"`
	Extensions map[string]interface     `json:"extensions,omitempty"`


// 参数的对齐
type Params struct 
	// The GraphQL type system to use when validating and executing a query.
	Schema Schema

	// A GraphQL language formatted string representing the requested operation.
	RequestString string

	// The value provided as the first argument to resolver functions on the top
	// level type (e.g. the query object type).
	RootObject map[string]interface

	// A mapping of variable name to runtime value to use for all variables
	// defined in the requestString.
	VariableValues map[string]interface

	// The name of the operation to use if requestString contains multiple
	// possible operations. Can be omitted if requestString contains only
	// one operation.
	OperationName string

	// Context may be provided to pass application-specific per-request
	// information to resolve functions.
	Context context.Context

3.3.2 对接前端

// graphql请求
query HeroNameAndFriends 
  hero(id:1001) 
    name
    friends 
      name
    
  


// QueryRequest GraphQL 查询语法字段
type QueryRequest struct 
	Query     string                 `json:"query" form:"query"`          //GraphQL query 语句
	Variables map[string]interface `json:"variables"  form:"variables"` //变量


func GraphQLHandler(c *gin.Context) 
	var reqData dto.QueryRequest
	if err := c.Bind(&reqData); err != nil 
		return
	

	//解析参数
	result := graphql.Do(graphql.Params
		Schema:         schema.Schema,
		RequestString:  reqData.Query,
		Context:        c,
		VariableValues: reqData.Variables,
	)
	if result.HasErrors() 
		return
	
	httprsp.Success(c, result.Data)

3.4 执行流程

3.5 文件结构

|
|---facade
|  	  |-router
|	  |-controller
|
|--assembler
|  	  |-schema:定义Fields、入参、出参
|     |-resolver:定义解析器逻辑

以上是关于GraphQL & Go,graphql基本知识,go-graphql使用的主要内容,如果未能解决你的问题,请参考以下文章

go-graphql

AWS GraphQL Appsync - 无法担任角色

Go mod graphql-go 的 Replace

基本操作:Go创建GraphQL API

Go 使用 GraphQL - 基础教程

GraphQL - 使用对象作为参数