之前 在之前写分页功能时候,曾经添加过一个count的小接口,可以通过GraphQL来查询news的条目数。这里我们就以此来实例说明如何使用GraphQL来做一个接受一个查询。
这个接口具备两种逻辑处理。
当没有传递指定标签id时候返回全部条目
指定标签id时候返回指定id的条目数
过程 类型系统 在进行查询之前我们需要对字段类型进行约束。 文件: data/types/NewsCountType.js
1 2 3 4 5 6 7 8 9 10 11 12 13 import { GraphQLObjectType as ObjectType, GraphQLInt as IntType, } from 'graphql' ; const NewsCountType = new ObjectType({ name: 'NewsCount' , fields: { count:{type :IntType} }, }); export default NewsCountType;
这里使用GraphQLObjectType来定义了返回的是一个对象,GraphQLInt则定义了返回的count属性是一个数字。 当然,这里只是非常的简单了使用了一个小案例。这里列举一下更多的类型定义。
GraphQLScalarType :Scalar Types,标量类型。最细粒度的类型
GraphQLInt:Int
GraphQLFloat:Float
GraphQLString:String
GraphQLBoolean:Boolean
GraphQLID:ID
GraphQLObjectType :对象
GraphQLInterfaceType :接口
GraphQLUnionType :联合
GraphQLEnumType :枚举
GraphQLInputObjectType :输入对象
GraphQLList :列表
GraphQLNonNull :NonNull
GraphQLObjectType 几乎所有的GraphQL都是Object对象。它具有一个name属性和一个非常重要的fields属性,这里定义了它的数据结构。就像之前,贴出的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var AddressType = new GraphQLObjectType({ name: 'Address' , fields: { street: { type : GraphQLString }, number: { type : GraphQLInt }, formatted: { type: GraphQLString, resolve (obj ) { return obj.number + ' ' + obj.street } } } }); var PersonType = new GraphQLObjectType({ name: 'Person' , fields: () => ({ name: { type : GraphQLString }, bestFriend: { type : PersonType }, }) });
GraphQLInterfaceType (这块暂时不是太理解,见谅) When a field can return one of a heterogeneous set of types, a Interface type is used to describe what types are possible, what fields are in common across all types, as well as a function to determine which type is actually used when the field is resolved.
1 2 3 4 5 6 var EntityType = new GraphQLInterfaceType({ name: 'Entity' , fields: { name: { type : GraphQLString } } });
GraphQLUnionType 当一个字段可以返回一个异构集合的类型中的一种,一个Union类型用于描述什么类型是可能的,以及提供一个函数,以确定哪些类型时该字段被解析实际使用。
1 2 3 4 5 6 7 8 9 10 11 12 var PetType = new GraphQLUnionType({ name: 'Pet' , types: [ DogType, CatType ], resolveType (value ) { if (value instanceof Dog) { return DogType; } if (value instanceof Cat) { return CatType; } } });
GraphQLEnumType 可枚举的数据类型
1 2 3 4 5 6 7 8 var RGBType = new GraphQLEnumType({ name: 'RGB' , values: { RED: { value : 0 }, GREEN: { value : 1 }, BLUE: { value : 2 } } });
为了查询对象定义的数据类型.
1 2 3 4 5 6 7 8 var GeoPoint = new GraphQLInputObjectType({ name: 'GeoPoint' , fields: { lat: { type : new GraphQLNonNull(GraphQLFloat) }, lon: { type : new GraphQLNonNull(GraphQLFloat) }, alt: { type : GraphQLFloat, defaultValue : 0 }, } });
GraphQLList 列表是其他类型的封装,通常用于对象字段的描述。
1 2 3 4 5 6 7 const PersonType = new GraphQLObjectType({ name: 'Person' , fields: () => ({ parents: { type : new GraphQLList(Person) }, children: { type : new GraphQLList(Person) }, }) });
GraphQLNonNull Non-Null 强制类型的值不能为 null.在请求出错时会报错。
小结 类型系统是GraphQL重要的组成部分之一,它规定了数据的类型。如果数据不符合指定的类型,GraphQL将会自动进行报错。这对调试接口非常重要。同时可以强制接口符合约定,保证代码的健壮性。
查询 GraphQL的查询语句相信是GraphQL最吸引人的地方,我个人认为,没有之一。 这里简单记录一下自己近段时间做的东西。主要包含前端和后端的实现,没有用到的地方就暂时不记录了。 这是获取数据的代码部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 resetNewsState:function (context,currentPage,pageSize,hotTag ) { const self = this ; fetch('/graphql' , { method: 'post' , headers: { Accept: 'application/json' , 'Content-Type' : 'application/json' }, body: JSON .stringify({ query: `{news( currentPage:${currentPage||this .props.currentPage} , pageSize:${pageSize||this .props.pageSize} , hotTag:${hotTag||9999 } ){title,link,contentSnippet}, tags{name,id}, count( hotTag:${hotTag||9999 } ){count}}` }), credentials: 'include' }).then(async function (resp ) { let { data } = await resp.json(); self.props.setRuntime({ name:"newsTotalItem" , value:data.count.count }); (context||this ).setState({ news:data.news, tags:data.tags, newsLoading:false }); }); },
其中 query
就是其中关键的查询代码。GraphQL的查询代码个人认为如果做好了配置,那么几乎是所见即所得的。如果这样还不太好理解。那么我就贴一下请求参数的反悔响应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 //request "{ news( currentPage:1, pageSize:10, hotTag:9999 ){title,link,contentSnippet}, tags{name,id}, count( hotTag:9999 ){count}}" //response { "data": { "news": [ { "title": "测试标题1", "link": "http://www.baidu.com", "contentSnippet": "测试摘要测试摘要" }, { "title": "测试标题2", "link": "http://www.baidu.com", "contentSnippet": "测试摘要测试摘要" }, ], "tags": [ { "name": "javascript", "id": "1" }, { "name": "css", "id": "2" } ], "count": { "count": 17 } } }
是不是很容易理解呢?通过编写查询语句,几乎就可以前端自己设计好需要的需要的json数据,从而优化请求。当然,这紧紧是前端部分。当我们使用以下代码查询条目数的时候,指定了hotTag的id为9999(这里是一个特殊标志符,代表查询全部hotTag)。
1 2 3 count( hotTag:9999 ){count}}
其中, count:{type:IntType}
规定了hotTag参数必须为数字。这个在第一个代码片段有写。但是这紧紧是前端的查询,为了获取到想要的数据,我们还需要后端也做好相应的查询操作。
以下贴出查询的完整查询代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 import { GraphQLInt } from 'graphql' ;import fetch from '../../core/fetch' ;import NewsCountType from '../types/NewsCountType' ;const url = '/api/news' ;let count = 0 ;let lastFetchTask;const newsCount = { type: NewsCountType, args: { hotTag:{ type : GraphQLInt} }, async resolve (reqObj,{hotTag} ) { lastFetchTask = await fetch(url,{ method: 'post' , headers: { Accept: 'application/json' , 'Content-Type' : 'application/json' , }, body: JSON .stringify({hotTag}), credentials: 'include' , }) .then(response => response.json()) .then(data => { if (data.code === 200 ) { count = data.count; } return {count}; }) .finally(() => { lastFetchTask = null ; }); if (count.length) { return {count}; } return lastFetchTask; }, }; export default newsCount;import { GraphQLSchema as Schema, GraphQLObjectType as ObjectType, } from 'graphql' ; import tags from './queries/tags' ;import content from './queries/content' ;import news from './queries/news' ;import count from './queries/newsCount' ;const schema = new Schema({ query: new ObjectType({ name: 'Query' , fields: { tags, content, news, count }, }), }); export default schema;
src/data/queries/newsCount.js文件是对数据进行具体查询和定义参数的文件,而src/data/schema.js则导出了一个Schema。
在这里我们可以发散一下,当我们要使用和定义后端的GraphQL相关,那么该怎么做呢: 我们需要一个GraphGL的Schema。它有以下结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const schema = new Schema({ query: new ObjectType({ name: 'Query' , fields: { count:{ type: NewsCountType, args: { hotTag:{ type : GraphQLInt} }, resolve:function (reqObj,paramObj ) { } } }, }), });
结合我们的类型系统,是不是很有感觉了呢?是的,当我们反复循环嵌套相关的数据类型,然后使用Schema返回。那么就可以配合前两篇文章写到的。从前端后端两个方面来实现GraphQL的对接。
收个尾 写到这里心里其实有些索然。因为感觉没有说到很深入的境地。但是怎么说呢,通过这篇文章好好体会一下,应该也可以在react-starter-kit里面使用GraphQL了,毕竟正常使用应该也用不到太过复杂的功能。
然而毕竟事情未能尽善,从第一篇到这一篇,期间磨磨蹭蹭一个月也过去了,新东西太多太快但是时间精力有限。限于个人水平文章也无法写的很通俗,但是确实是用心去做了。
有时间把源代码整理完毕一起放出来供参考。