精华内容
参与话题
问答
  • GraphQL入门有这一篇就足够了

    万次阅读 多人点赞 2018-10-08 13:39:16
    本文将从GraphQL是什么,为什么要使用GraphQL,使用GraphQL创建简单例子,以及GraphQL实战,四个方面对GraphQL进行阐述。说得不对的地方,希望大家指出斧正。 github项目地址:...

    本文将从GraphQL是什么,为什么要使用GraphQL,使用GraphQL创建简单例子,以及GraphQL实战,四个方面对GraphQL进行阐述。说得不对的地方,希望大家指出斧正。

    github项目地址:https://github.com/Charming2015/graphql-todolist

    一、GraphQL是什么?

    关于GraphQL是什么,网上一搜一大堆。根据官网的解释就是一种用于 API 的查询语言

    一看到用于API的查询语言,我也是一脸懵逼的。博主你在开玩笑吧?你的翻译水平不过关?API还能查吗?API不是后端写好,前端调用的吗?

    的确可以,这就是GraphQL强大的地方。
    引用官方文档的一句话:

    ask exactly what you want.

    二、为什么要使用GraphQL?

    在实际工作中往往会有这种情景出现:比如说我需要展示一个游戏名的列表,可接口却会把游戏的详细玩法,更新时间,创建者等各种各样的 (无用的) 信息都一同返回。

    问了后端,原因大概如下:

    • 原来是为了兼容PC端和移动端用同一套接口
    • 或者在整个页面,这里需要显示游戏的标题,可是别的地方需要显示游戏玩法啊,避免多次请求我就全部返回咯
    • 或者是因为有时候项目经理想要显示“标题+更新时间”,有时候想要点击标题展开游戏玩法等等需求,所以把游戏相关的信息都一同返回

    简单说就是:

    • 兼容多平台导致字段冗余
    • 一个页面需要多次调用 API 聚合数据
    • 需求经常改动导致接口很难为单一接口精简逻辑

    有同学可能会说那也不一定要用GraphQL啊,比方说第一个问题,不同平台不同接口不就好了嘛

    http://api.xxx.com/web/getGameInfo/:gameID
    http://api.xxx.com/app/getGameInfo/:gameID
    http://api.xxx.com/mobile/getGameInfo/:gameID
    

    或者加个参数也行

    http://api.xxx.com/getGameInfo/:gameID?platfrom=web
    

    这样处理的确可以解决问题,但是无疑加大了后端的处理逻辑。你真的不怕后端程序员打你?

    这个时候我们会想,接口能不能不写死,把静态变成动态?

    回答是可以的,这就是GraphQL所做的!

    三、GraphQL尝尝鲜——(GraphQL简单例子)

    下面是用GraphQL.jsexpress-graphql搭建一个的普通GraphQL查询(query)的例子,包括讲解GraphQL的部分类型和参数,已经掌握了的同学可以跳过。

    1. 先跑个hello world
    1. 新建一个graphql文件夹,然后在该目录下打开终端,执行npm init --y初始化一个packjson文件。
    2. 安装依赖包:npm install --save -D express express-graphql graphql
    3. 新建schema.js文件,填上下面的代码
    //schema.js
    const {
          GraphQLSchema,
          GraphQLObjectType,
          GraphQLString,
        } = require('graphql');
    const queryObj = new GraphQLObjectType({
        name: 'myFirstQuery',
        description: 'a hello world demo',
        fields: {
            hello: {
                name: 'a hello world query',
                description: 'a hello world demo',
                type: GraphQLString,
                resolve(parentValue, args, request) {
                    return 'hello world !';
                }
            }
        }
    });
    module.exports = new GraphQLSchema({
      query: queryObj
    });
    

    这里的意思是新建一个简单的查询,查询名字叫hello,会返回字段hello world !,其他的是定义名字和查询结果类型的意思。

    1. 同级目录下新建server.js文件,填上下面的代码
    // server.js
    const express = require('express');
    const expressGraphql = require('express-graphql');
    const app = express();
    
    const schema = require('./schema');
    app.use('/graphql', expressGraphql({
    	schema,
    	graphiql: true
    }));
    
    app.get('/', (req, res) => res.end('index'));
    
    app.listen(8000, (err) => {
      if(err) {throw new Error(err);}
      console.log('*** server started ***');
    });
    

    这部分代码是用express跑起来一个服务器,并通过express-graphqlgraphql挂载到服务器上。

    1. 运行一下node server,并打开http://localhost:8000/

    这里写图片描述

    如图,说明服务器已经跑起来了
    打开http://localhost:8000/graphql,是类似下面这种界面说明已经graphql服务已经跑起来了!
    这里写图片描述
    在左侧输入 (graphql的查询语法这里不做说明)

    {
      hello
    }
    

    点击头部的三角形的运行按钮,右侧就会显示你查询的结果了
    这里写图片描述

    2. 不仅仅是hello world

    先简单讲解一下代码:

    const queryObj = new GraphQLObjectType({
        name: 'myFirstQuery',
        description: 'a hello world demo',
        fields: {}
    });
    

    GraphQLObjectType是GraphQL.js定义的对象类型,包括namedescriptionfields三个属性,其中namedescription 是非必填的。fields是解析函数,在这里可以理解为查询方法

    hello: {
                name: 'a hello world query',
                description: 'a hello world demo',
                type: GraphQLString,
                resolve(parentValue, args, request) {
                    return 'hello world !';
                }
            }
    

    对于每个fields,又有name,description,type,resolve参数,这里的type可以理解为hello方法返回的数据类型,resolve就是具体的处理方法。

    说到这里有些同学可能还不满足,如果我想每次查询都想带上一个参数该怎么办,如果我想查询结果有多条数据又怎么处理?

    下面修改schema.js文件,来一个加强版的查询(当然,你可以整理一下代码,我这样写是为了方便阅读)

    const {
          GraphQLSchema,
          GraphQLObjectType,
          GraphQLString,
          GraphQLInt,
          GraphQLBoolean
        } = require('graphql');
    
    const queryObj = new GraphQLObjectType({
        name: 'myFirstQuery',
        description: 'a hello world demo',
        fields: {
            hello: {
                name: 'a hello world query',
                description: 'a hello world demo',
                type: GraphQLString,
                args: {
                    name: {  // 这里定义参数,包括参数类型和默认值
                        type: GraphQLString,
                        defaultValue: 'Brian'
                    }
                },
                resolve(parentValue, args, request) { // 这里演示如何获取参数,以及处理
                    return 'hello world ' + args.name + '!';
                }
            },
            person: {
                name: 'personQuery',
                description: 'query a person',
                type: new GraphQLObjectType({ // 这里定义查询结果包含name,age,sex三个字段,并且都是不同的类型。
                    name: 'person',
                    fields: {
                      name: {
                        type: GraphQLString
                      },
                      age: {
                        type: GraphQLInt
                      },
                      sex: {
                        type: GraphQLBoolean
                      }
                    }
                }),
                args: {
                    name: {
                        type: GraphQLString,
                        defaultValue: 'Charming'
                    }
                },
                resolve(parentValue, args, request) {
                    return {
                        name: args.name,
                        age: args.name.length,
                        sex: Math.random() > 0.5
                    };
                }
            }
        }
    });
    
    module.exports = new GraphQLSchema({
      query: queryObj 
    });
    

    重启服务后,继续打开http://localhost:8000/graphql,在左侧输入

    {
      hello(name:"charming"),
      person(name:"charming"){
        name,
        sex,
        age
      }
    }
    

    右侧就会显示出:
    这里写图片描述

    你可以在左侧仅输入person方法的sexage两个字段,这样就会只返回sexage的信息。动手试一试吧!

    {
      person(name:"charming"){
        sex,
        age
      }
    }
    

    当然,结果的顺序也是按照你输入的顺序排序的。

    定制化的数据,完全根据你查什么返回什么结果。这就是GraphQL被称作API查询语言的原因。

    四、GraphQL实战

    下面我将搭配koa实现一个GraphQL查询的例子,逐步从简单koa服务到mongodb的数据插入查询,再到GraphQL的使用,最终实现用GraphQL对数据库进行增删查改。

    项目效果大概如下:
    这里写图片描述

    有点意思吧?那就开始吧~
    先把文件目录建构建好

    1. 初始化项目

    • 初始化项目,在根目录下运行npm init --y
    • 然后安装一些包:npm install koa koa-static koa-router koa-bodyparser --save -D
    • 新建configcontrollersgraphqlmongodbpublicrouter这几个文件夹。装逼的操作是在终端输入mkdir config controllers graphql mongodb public router回车,ok~

    2. 跑一个koa服务器
    新建一个server.js文件,写入以下代码

    // server.js
    import Koa from 'koa'
    import Router from 'koa-router'
    import bodyParser from 'koa-bodyparser'
    
    const app = new Koa()
    const router = new Router();
    const port = 4000
    
    app.use(bodyParser());
    
    router.get('/hello', (ctx, next) => {
      ctx.body="hello world"
    });
    
    
    app.use(router.routes())
       .use(router.allowedMethods());
    
    app.listen(port);
    
    console.log('server listen port: ' + port)
    

    执行node server跑起来服务器,发现报错了:
    这里写图片描述

    这是正常的,这是因为现在的node版本并没有支持es6的模块引入方式。

    百度一下就会有解决方案了,比较通用的做法是用babel-polyfill进行转译。

    详细的可以看这一个参考操作:How To Enable ES6 Imports in Node.JS

    具体操作是:新建一个start.js文件,写入:

    // start.js
    require('babel-register')({
        presets: [ 'env' ]
    })
    require('babel-polyfill')
    require('./server.js')
    

    安装相关包:npm install --save -D babel-preset-env babel-polyfill babel-register

    修改package.json文件,把"start": "start http://localhost:4000 && node start.js"这句代码加到下面这个位置:
    这里写图片描述

    运行一下npm run start,打开http://localhost:4000/hello,结果如图:
    这里写图片描述
    说明koa服务器已经跑起来了。

    那么前端页面呢?

    (由于本文内容不是讲解前端,所以前端代码自行去github复制)

    • 在public下新建index.html文件和js文件夹,代码直接查看我的项目public目录下的 index.htmlindex-s1.js 文件
    • 修改server.js,引入koa-static模块。koa-static会把路由的根目录指向自己定义的路径(也就是本项目的public路径)
    //server.js
    import Koa from 'koa'
    import Router from 'koa-router'
    import KoaStatic from 'koa-static'
    import bodyParser from 'koa-bodyparser'
    
    const app = new Koa()
    const router = new Router();
    const port = 4000
    
    app.use(bodyParser());
    
    router.get('/hello', (ctx, next) => {
      ctx.body="hello world"
    });
    
    
    app.use(KoaStatic(__dirname + '/public'));
    app.use(router.routes())
       .use(router.allowedMethods());
    
    app.listen(port);
    
    console.log('server listen port: ' + port)
    

    打开http://localhost:4000/,发现是类似下面的页面:
    这里写图片描述

    这时候页面已经可以进行简单的交互,但是还没有和后端进行数据交互,所以是个静态页面。

    3. 搭一个mongodb数据库,实现数据增删改查

    注意: 请先自行下载好mongodb并启动mongodb。

    a. 写好链接数据库的基本配置和表设定

    config文件夹下面建立一个index.js,这个文件主要是放一下链接数据库的配置代码。

    //  config/index.js
    export default {
      dbPath: 'mongodb://localhost/todolist'
    }
    

    mongodb文件夹新建一个index.jsschema文件夹, 在 schema文件夹文件夹下面新建list.js

    mongodb/index.js下写上链接数据库的代码,这里的代码作用是链接上数据库

    // mongodb/index.js
    import mongoose from 'mongoose'
    import config from '../config'
    
    require('./schema/list')
    
    export const database = () => {
      mongoose.set('debug', true)
    
      mongoose.connect(config.dbPath)
    
      mongoose.connection.on('disconnected', () => {
        mongoose.connect(config.dbPath)
      })
      mongoose.connection.on('error', err => {
        console.error(err)
      })
    
      mongoose.connection.on('open', async () => {
        console.log('Connected to MongoDB ', config.dbPath)
      })
    }
    

    mongodb/schema/list.js定义表和字段:

    //mongodb/schema/list.js
    import mongoose from 'mongoose'
    
    const Schema = mongoose.Schema
    const ObjectId = Schema.Types.ObjectId
    
    const ListSchema = new Schema({
      title: String,
      desc: String,
      date: String,
      id: String,
      checked: Boolean,
      meta: {
        createdAt: {
          type: Date,
          default: Date.now()
        },
        updatedAt: {
          type: Date,
          default: Date.now()
        }
      }
    })
    
    ListSchema.pre('save', function (next) {// 每次保存之前都插入更新时间,创建时插入创建时间
      if (this.isNew) {
        this.meta.createdAt = this.meta.updatedAt = Date.now()
      } else {
        this.meta.updatedAt = Date.now()
      }
      next()
    })
    mongoose.model('List', ListSchema)
    

    b. 实现数据库增删查改的控制器

    建好表,也链接好数据库之后,我们就要写一些方法来操作数据库,这些方法都写在控制器(controllers)里面。

    controllers里面新建list.js,这个文件对应操作list数据的控制器,单独拿出来写是为了方便后续项目复杂化的模块化管理。

    // controllers/list.js
    import mongoose from 'mongoose'
    const List = mongoose.model('List')
    // 获取所有数据
    export const getAllList = async (ctx, next) => {
      const Lists = await List.find({}).sort({date:-1}) // 数据查询
      if (Lists.length) {
        ctx.body = {
          success: true,
          list: Lists
        }
      } else {
        ctx.body = {
          success: false
        }
      }
    }
    // 新增
    export const addOne = async (ctx, next) => {
      // 获取请求的数据
      const opts = ctx.request.body
      
      const list = new List(opts)
      const saveList = await list.save() // 保存数据
      console.log(saveList)
      if (saveList) {
        ctx.body = {
          success: true,
          id: opts.id
        }
      } else {
        ctx.body = {
          success: false,
          id: opts.id
        }
      }
    }
    // 编辑
    export const editOne = async (ctx, next) => {
      const obj = ctx.request.body
      let hasError = false
      let error = null
      List.findOne({id: obj.id}, (err, doc) => {
      	if(err) {
      		hasError = true
      		error = err
      	} else {
      		doc.title = obj.title;
      		doc.desc = obj.desc;
      		doc.date = obj.date;
      		doc.save();
      	}
      })
      if (hasError) {
      	ctx.body = {
          success: false,
          id: obj.id
        }
      } else {
      	ctx.body = {
    	  success: true,
        id: obj.id
    	}
      }
    }
    
    // 更新完成状态
    export const tickOne = async (ctx, next) => {
      const obj = ctx.request.body
      let hasError = false
      let error = null
      List.findOne({id: obj.id}, (err, doc) => {
      	if(err) {
      		hasError = true
      		error = err
      	} else {
      		doc.checked = obj.checked;
      		doc.save();
      	}
      })
      if (hasError) {
      	ctx.body = {
          success: false,
          id: obj.id
        }
      } else {
      	ctx.body = {
    	  success: true,
        id: obj.id
    	}
      }
    }
    
    // 删除
    export const delOne = async (ctx, next) => {
      const obj = ctx.request.body
      let hasError = false
      let msg = null
      List.remove({id: obj.id}, (err, doc) => {
      	if(err) {
      		hasError = true
      		msg = err
      	} else {
      		msg = doc
      	}
      })
      if (hasError) {
      	ctx.body = {
          success: false,
          id: obj.id
        }
      } else {
      	ctx.body = {
      	  success: true,
          id: obj.id
      	}
      }
    }
    

    c. 实现路由,给前端提供API接口

    数据模型和控制器都已经设计好了,下面就利用koa-router路由中间件,来实现请求的接口。

    我们回到server.js,在上面添加一些代码。如下:

    // server.js
    import Koa from 'koa'
    import Router from 'koa-router'
    import KoaStatic from 'koa-static'
    import bodyParser from 'koa-bodyparser'
    import {database} from './mongodb' 
    import {addOne, getAllList, editOne, tickOne, delOne} from './controllers/list' 
    
    database() // 链接数据库并且初始化数据模型
    
    const app = new Koa()
    const router = new Router();
    const port = 4000
    
    app.use(bodyParser());
    
    router.get('/hello', (ctx, next) => {
      ctx.body = "hello world"
    });
    
    // 把对请求的处理交给处理器。
    router.post('/addOne', addOne)
          .post('/editOne', editOne)
          .post('/tickOne', tickOne)
          .post('/delOne', delOne)
          .get('/getAllList', getAllList)
    
    app.use(KoaStatic(__dirname + '/public'));
    app.use(router.routes())
       .use(router.allowedMethods());
    
    app.listen(port);
    
    console.log('server listen port: ' + port)
    

    上面的代码,就是做了:

    1. 引入mongodb设置、list控制器,
    2. 链接数据库
    3. 设置每一个设置每一个路由对应的我们定义的的控制器。
    

    安装一下mongoose:npm install --save -D mongoose

    运行一下npm run start,待我们的服务器启动之后,就可以对数据库进行操作了。我们可以通过postman来模拟请求,先插几条数据:
    这里写图片描述
    查询全部数据:
    这里写图片描述

    d. 前端对接接口

    前端直接用ajax发起请求就好了,平时工作中都是用axios的,但是我懒得弄,所以直接用最简单的方法就好了。

    引入了JQuery之后,改写public/js/index.js文件:略(项目里的public/index-s2.js的代码)

    项目跑起来,发现已经基本上实现了前端发起请求对数据库进行操作了。
    至此你已经成功打通了前端后台数据库,可以不要脸地称自己是一个小全栈了!

    不过我们的目的还没有达到——用grapql实现对数据的操作!

    4. 用grapql实现对数据的操作

    GraphQL 的大部分讨论集中在数据获取(query),但是任何完整的数据平台也都需要一个改变服务端数据的方法。
    REST 中,任何请求都可能最后导致一些服务端副作用,但是约定上建议不要使用 GET 请求来修改数据。GraphQL 也是类似 —— 技术上而言,任何查询都可以被实现为导致数据写入。然而,建一个约定来规范任何导致写入的操作都应该显式通过变更(mutation)来发送。

    简单说就是,GraphQL用mutation来实现数据的修改,虽然mutation能做的query也能做,但还是要区分开这连个方法,就如同REST中约定用GET来请求数据,用其他方法来更新数据一样。

    a. 实现查询
    查询的话比较简单,只需要在接口响应时,获取数据库的数据,然后返回;

    const objType = new GraphQLObjectType({
      name: 'meta',
      fields: {
        createdAt: {
          type: GraphQLString
        },
        updatedAt: {
          type: GraphQLString
        }
      }
    })
    let ListType = new GraphQLObjectType({
      name: 'List',
      fields: {
        _id: {
          type: GraphQLID
        },
        id: {
          type: GraphQLString
        },
        title: {
          type: GraphQLString
        },
        desc: {
          type: GraphQLString
        },
        date: {
          type: GraphQLString
        },
        checked: {
          type: GraphQLBoolean
        },
        meta: {
          type: objType
        }
      }
    })
    const listFields = {
      type: new GraphQLList(ListType),
      args: {},
      resolve (root, params, options) {
        return List.find({}).exec() // 数据库查询
      }
    }
    let queryType = new GraphQLObjectType({
        name: 'getAllList',
        fields: {
          lists: listFields,
        }
    })
    
    export default new GraphQLSchema({
      query: queryType
    })
    

    把增删查改都讲完再更改代码~
    b. 实现增删查改

    一开始说了,其实mutationquery用法上没什么区别,这只是一种约定。
    具体的mutation实现方式如下:

    const outputType = new GraphQLObjectType({
      name: 'output',
      fields: () => ({
        id:     { type: GraphQLString},
        success:   { type: GraphQLBoolean },
      })
    });
    
    const inputType = new GraphQLInputObjectType({
      name: 'input',
      fields: () => ({
        id:          { type: GraphQLString },
        desc:        { type: GraphQLString },
        title:       { type: GraphQLString },
        date:        { type: GraphQLString },
        checked:        { type: GraphQLBoolean }
      })
    });
    let MutationType = new GraphQLObjectType({
      name: 'Mutations',
      fields: () => ({
        delOne: {
          type: outputType,
          description: 'del',
          args: {
            id: { type: GraphQLString }
          },
          resolve: (value, args) => {
          	console.log(args)
          	let result = delOne(args)
            return result
          }
        },
        editOne: {
          type: outputType,
          description: 'edit',
          args: {
            listObj: { type: inputType }
          },
          resolve: (value, args) => {
          	console.log(args)
          	let result = editOne(args.listObj)
            return result
          }
        },
        addOne: {
          type: outputType,
          description: 'add',
          args: {
            listObj: { type: inputType }
          },
          resolve: (value, args) => {
          	console.log(args.listObj)
          	let result = addOne(args.listObj)
            return result
          }
        },
        tickOne: {
          type: outputType,
          description: 'tick',
          args: {
            id: { type: GraphQLString },
            checked: { type: GraphQLBoolean },
          },
          resolve: (value, args) => {
          	console.log(args)
          	let result = tickOne(args)
            return result
          }
        },
    
      }),
    });
    
    export default new GraphQLSchema({
      query: queryType,
      mutation: MutationType
    })
    

    c. 完善其余代码

    在实现前端请求Graphql服务器时,最困扰我的就是参数以什么样的格式进行传递。后来在Graphql界面玩Graphql的query请求时发现了其中的诀窍…

    关于前端请求格式进行一下说明:
    这里写图片描述
    如上图,在玩Graphql的请求时,我们就可以直接在控制台network查看请求的格式了。这里我们只需要模仿这种格式,当做参数发送给Graphql服务器即可。

    记得用反引号: `` ,来拼接参数格式。然后用data: {query: params}的格式传递参数,代码如下:

    let data = {
                query: `mutation{
                          addOne(listObj:{
                            id: "${that.getUid()}",
                            desc: "${that.params.desc}",
                            title: "${that.params.title}",
                            date: "${that.getTime(that.params.date)}",
                            checked: false
                          }){
                            id,
                            success
                          }
                        }`
              }
              $.post('/graphql', data).done((res) => {
                console.log(res)
                // do something
              })
    

    最后更改server.jsrouter/index.jscontrollers/list.jspublic/index.js改成github项目对应目录的文件代码即可。

    完整项目的目录如下:
    在这里插入图片描述

    五、后记

    对于Vue开发者,可以使用vue-apollo使得前端传参更加优雅~

    六、参考文献

    展开全文
  • GraphQL:新的API标准、查询语言

    千人学习 2019-05-11 18:06:24
    GraphQL是一种新的API标准,它提供了一种更高效、强大和灵活的数据提供方式。 GraphQL是api的查询语言,而不是数据库。 可以在使用API的任何环境中有效使用,我们可以理解为GraphQL是基于API之上的一层封装,目的是...
  • 最近打算写一个关于GraphQL的系列,总结一下在开发中对GraphQL服务的使用,同时也希望能对初次使用GraphQL以及正在使用GraphQL的童鞋提供一丢丢帮助。这个系列目前大致书写方向如下: GraphQL入门 SpringBoot对...

    前言:

    最近打算写一个关于GraphQL 的系列,总结一下在开发中对GraphQL服务的使用,同时也希望能对初次使用GraphQL以及正在使用GraphQL的童鞋提供一丢丢帮助。这个系列目前大致书写方向如下:

    GraphQL入门

    SpringBoot对GraphQL Java的集成开发环境的构建

    GraphQL 在前端框架vue.js中的应用

    GraphQL在实际开发中遇到的一些用法扩展(比如Scalar)、问题处理、需求处理、源码解析等

    -----------------------------------------------------入门开始------------------------------------------------------------

    1、GraphQL是什么?

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑,GraphQL 可以运行在任何后端框架或者编程语言之上。

    上面这句释义来至GraphQL官网,不清楚类型系统(type system)的话,读起来可能会有点涩。。往后看就懂了。

    2、为什么要是用GraphQL

    GraphQL可以理解为是一个基于新的API标准,或者说基于对RESTful的封装的一种API查询语言。再过去的很多年里,RESTful被当做了API设计的一种标准(这里说RESTfu是一种设计标准其实不准确,说它是一种软件架构风格、设计风格更好,它只是提供了一组设计原则和约束条件。暂且这么说了。),但是在客户需求快速变化的今天,RESTful API显得有些死板僵化。而GraphQL的推出就是为了针对性的解决客户端数据访问的灵活性和高效。

    怎么理解RESTful API 在适应当今复杂需求的时显露出来的僵化问题?或者说相比较而言Graphql API 的优势是什么呢?

    在开发中RESTful API接口返回的数据格式、数据类型都是后端预先定义好的,如果返回的数据格式并不是调用者(前端)理想型,前端一般会通过以下两种方式来解决:

    <1> 和后端沟通,改接口(更改数据源)

    <2> 前端自己做适配工作(处理数据源)

    一般如果是小型项目,或者说对应的是单前端需求,改后端接口比较好商量,对这个项目的有效运行影响不大。但是如果是大项目,譬如,我先在所在项目,一个后端API对应的是三接口,什么意思呢,就是一个后端API需要同时满足web、Android Q、IOS前端的不同的数据需求,这种情况下为了某一方需求要求改后端API,明显会顾此失彼,不现实。所以一般这种情况就需要前端自己做适配来满足自己的需求。

    正是为了适应当今愈加复杂的需求环境,Facebook推出了Graphql API,一个可以帮助前后端解耦的API查询语言,让接口的返回值从静态变为动态,即调用者(前端)来声明接口返回什么数据。

    举个例子:

    假设在一个共享汽车管理系统中,客户端进来的首页,需要同时显示的部分信息有:当前用户的用户名,当前用户的近一周的订单信息,当前用户的账户余额信息。如下需求:

    针对上面需求,RESTful API和GraphQL的处理方式是不同的。

    RESTful API 分三次请求完成:

            通过user.id 获取用户账号(zhxx)信息数据

            通过user.id获取用户钱包(qbxx)信息数据

            通过user.id获取用户订单(ddxx)信息数据

    GraphQL web客户端做一次下面请求就可:

    而且如果此时我的Android前端还需要上面zhxx数据中的age字段,那么Android前端只需在请求数据时,加上age字段即可:

    这就是Graphql官网所说的: ask exactly what you want! 

    通过上面描述,可以发现GraphQL API有如下优点:客户端可以自定义查询语句,数据获取灵活多变,服务端按需返回数据,减少网络的开销,提高了性能。而这些都是Restful API的弊端。

    3、鸟瞰GraphQL的整个查询服务代码架构

    GraphQL 其实就是想要什么,就传入需求,然后按需返回字段,具体字段处理是服务器所提供,,而 graphql 并不会关心服务器怎么处理。下面我先带大家看看一个GraphQL查询服务的整体流程是怎么走的,懂得GraphQL处理业务的流程,基本就知道怎么开始摸索开发了:

    3.1 先看看GraphQL服务的大致工作流程图:

    GraphQL

    3.2 来个具体案例实现上图业务处理流程:查询客户端登录的用户信息(只说大致流程,下篇详解开发细节。)

    (1) java服务端定义业务方法,查出所需数据,类似这样(这其实就是后面要讲的 Resolver(解析器)):

    (2)在.graphqls文件中定义所需数据的字段、字段类型,类似这样(这里就开始涉及Granphql类型系统的知识了):

    (2.1) 定义graphql查询方法

    (2.2)定义Client对象的类型属性

    (3) 前台按照约定,编写查询语句,发送请求取数据,如下:

    在上面这个查询请求中,我们可以将查询语句拆分为几个部分:

    <1> 我们从query这个查询的根对象开始定义查询,query是一个特殊的对象,这个对象名不是你自定义的,但是这个对象的属性,也就是之后出现所有子查询请求,都是由你来定义的。通俗的来说,query是所有子查询的入口,所有的查询请求都将从这里进入。

    <2> 这里前端告知需要查询client的数据,client是一个我自定义的查询对象,它表示的是客户的登录信息,在此刻它也变成了query查询对象里的一个属性了。现在我将开始查询client对象里面一些属性数据。

    <3> 现在前端告诉我,他只需要client里面的id,c_xm属性数据,其它的诸如性别,住址都不要。于是,整个graphql查询语句就写成了上面这样。

    4、一些关于Graphql 的重要概念解释:

    4.1 Granphql Schema 

    每一个 GraphQL 服务都会定义一套类型,用以描述你可能从那个服务查询到的数据。每当查询到来,服务器就会根据 schema 验证并执行查询。那么这里的说的schema什么?这关乎我们理解Granphql 类型系统,所以在说Granphql 类型系统之前,我们的先弄懂GraphQL Schema。

    与XML Schema 的概念类似,Schema由服务端来定义,用于定义API接口,并依靠Schema来生成文档以及对客户端请求进行校验。Schema只是一个概念,它是由各种数据类型及其字段组成,而每个类型的每个字段都有相应的函数来返回数据。如下面这个,就是一个Schema:

    type Query {
        #用户对象
        client:Client!
    }
    # 用户对象
    type Client{
      id: String
      login_id: String
      rid: String
      c_xm: String
    }
    

     上面定义了两个类型,Client和Query:Client有几个字段分别是id、Login_id、rid、c_xm;Query是个特殊的类型,是查询入口,所有要查询的都放着里面。Client类型中的字段都有自动生成的函数来返回数据,类似于这种:

    function Client_id(client) {
      return client.getId();
    }

    4.2 Granphql 类型系统(Type System)

    GraphQL作为一种应用层的查询语言,它有自己的数据类型,用来描述数据(换句话说:用它自己的数据类型来定义你想要查询的对象及对象的属性,类似Java定义属性或对象的数据类型:String name、JSONObject  data。Graphql定义查询对象和对象属性如上3.2的(2)部分提到的)。比如标量类型、集合类型、Object类型、枚举等,甚至有接口类型,这些构成了Graphql的Type System。

    GraphQL 服务可以用任何语言来编写运行,因为它并不依赖于任何编程语言的句法来与 GraphQL schema 沟通,它定义了自己的语言:GraphQL schema language —— 它和 GraphQL 的查询语言很相似,能够和 GraphQL schema 之间可以正常沟通。

     4.2.1 对象类型和字段(Object Types and Fields)

    一个 GraphQL schema 中的最基本的组件是对象类型,它表示你可以从服务器上获取到什么类型的对象,以及这个对象的所有属性字段(如果你需要它所有字段的话)。使用 GraphQL schema language,可以如下表示:

    #国家对象
    type Country{
      #国家名称
      name: String
      #省份
      province: String
    }
    • Country是一个 GraphQL 对象类型,同Java对象一样,它也会拥有一些字段。schema 中的大多数类型都会是对象类型。
    • name 和 province是 Country对象类型上的字段。当你查询Country对象时,就会获得有且仅有 name 和 province字段。
    • String 是内置的标量类型,标量类型下面详述。
    • # 号是GraphQL schema中用来注释代码的

    4.2.1 标量类型(Scalar Types)

    从上面说到的对象类型可以知道,一个对象类型同java对象一样,有自己的对象名和属性字段,它的属性字段也需要Graphql的类型系统给出具体类型来注释,以便同服务器的传递过来的不同类型的数据不起冲突。所以Graphql的类型系统便定义了标量类型,它是解析到单个标量对象的类型,无法在查询中对它进行次级选择,标量类型是不能包含子字段(通俗来讲,你在上面秒速文件中,按住ctrl键,鼠标点击String,点不进去了,但是你点击第一个Dwxx却可以点进去,并定位到下面的type Dwxx)。

    目前 GraphQL 自带的标量类型有如下几个(注意首字母):

    • Int:有符号 32 位整数。
    • Float:有符号双精度浮点值。
    • String:UTF‐8 字符序列。
    • Booleantrue 或者 false
    • ID:常用于获取数据的唯一标志,或缓存的键值,它也会被序列化为String,但可读性差

    除此之外,我们也可以自定以标量类型来满足我们的实际开发需求,比如,我可以定义一个名为 yyyymmdd 的yyyymmdd格式的日期标量类型,具体实现后面讲。

    4.2.2 集合 (List)

    在GraphQL规范中可以使用一个类型修饰符方括号:[] 来标记一个类型为 List,这有点像java中定义数组。它表示这个字段会返回这个类型的数组(集合)。

     例如下面这个例子,一个Country有很多个province,province就表示为一个集合或者说数组:

    #国家对象
    type Country{
      #国家名称
      name: String
      #省份
      province: [String]
    }

    4.2.3 空(null)、非空 (Non-Null)

    在GraphQL 规范中,通过在类型名后面添加一个叹号: ! 来将其标注为非空。这样后台服务器就必须要对这个字段返回一个非空的值。如果后台服务器返回了一个null给这个字段,GraphQL 就发生错误,同时客户端也会受到这个错误信息。

    例:

    #国家对象
    type Country{
      #国家名称
      name: String!
      #省份
      province: [String!]
      #市
      city:[String]!
      #乡镇
      town:[String!]!
    }
    • String! 后面加的这个英文叹号表示name这个字段是非空的,当你查询这个字段时Graphql必须要给你返回一个非null的值。
    • [String!] 表示province的返回值是一个非空字符串的数组(集合)。即数组本身可以为空,但是其不能有任何空值成员,详细参照下面:
    province: null // 有效
    province: [] // 有效
    province: ['a', 'b'] // 有效
    province: ['a', null, 'b'] // 错误
    • [String]!表示city的返回值是一个不可为空的字符串数组(集合)(注意同上面的语言表达有异)。即数组本身不能为空,但是其可以包含空值成员,详细参照下面:
    city: null // 错误
    city: [] // 有效
    city: ['a', 'b'] // 有效
    city: ['a', null, 'b'] // 有效
    • [String!]! 表示town的值是一个 非空字符串的非空数组(集合)。即返回的集合不能为空,集合中也不能存在空值。参考下面:
    city: null // 错误
    city: [] // 错误
    city: ['a', 'b'] // 有效
    city: ['a', null, 'b'] // 错误

     4.2.4  参数(Arguments)

    实际开发中,我们可能需要前端提交查询请求的同时,传递参数给我们供我们后台服务器拿去执行查询。

    举个例子,现有这样一个需求背景:前端需要查询单位信息数据,后台提供的查询接口,支持前台输入单位代码(dwdm),机构名(jgm)等参数来精确查询,其中单位代码是比输入项,机构名为选填。那么graphql的描述文件(或者说schema )可以这样写:

    type query{
        
        #查询单位信息
        dwxx(
            #单位代码,必填
            dwdm:String!
            #单位名称
            dwmc:String
            ):Dwxx
    }
    
    type Dwxx{
        id:Int
        ..
        这里是要返回的单位信息对象类型的一些属性字段
    
    }

    4.3 Resolver

    如果你仅在Schema中的Query{ }查询接口中声明了若干子query函数和定义它的返回值类型,那么此刻你只完成了一半的工作,因为你还需要提供相关Query所返回数据的后台逻辑。所以为了能够使GraphQL正常工作,你还需要再了解一个核心概念——Resolver(解析函数)

    GraphQL中官方文档中对于每个子Query和与之对应的Resolver有明确的定义规范,以确保GraphQL能把Schema中每个子query同它们的Resolver一一对应起来,最终实现后端逻辑处理数据后丢给Graphql,Graphql再返回数据给前端。

    Granphql官方文档中,Resolver命名规范如下(注意红色部分就是方法名命名格式,任选其一就可):

    <fieldname>(dataRepositoryClassInstance, *fieldArgs [, DataFetchingEnvironment])
    is<Fieldname>(dataRepositoryClassInstance, *fieldArgs [, DataFetchingEnvironment])
    get<Fieldname>(dataRepositoryClassInstance, *fieldArgs [, DataFetchingEnvironment])
    getField<Fieldname>(dataRepositoryClassInstance, *fieldArgs [, DataFetchingEnvironment])

    就上面规范举个例子,比如关于

    type Query {
        #用户对象
        client:Client!
    }

    这Query中的子query client, 它的Resolver的名字可以为client、isClient、getClient、getFieldClient,一般我会直接选第一种方式。

    5、鸟瞰 Granphql 的内部运行机制

    我这里写了一个Schema文件来定义返回值type,type Query{ }部分代码就不写了,实际上type Query{ }这个查询入口可以单独写个Schema文件,和type car这种类型定义的Schema文件区分开来,这样代码可读性更强(在type Query{ }的Schema文件中按住Ctrl键,鼠标单击 car ,是可以跳到type car { }里面的,和java一样,可以实现代码追踪)。好了,先看下面type定义:

    #汽车
    type car{
      #车辆编号
      id: ID
      #备注
      txt: String
      #生产商
      product: company
    }
    
    #汽车生产商
    type company{
      #生产商编码
      id: ID
      #生产商名字
      name: String
    }

    此时前端开始请求数据:

    现在我们来看看这段请求,GraphQL API是如何解析这段查询语句的:

    • 首先进行第一层解析,当前GraphQL QueryRoot Query类型是query(Root Query类型一种有三个,还有mutation 更改,subscription 订阅 这两个类型入口,暂不描述。),同时需要它的子query名字是 car
    • 之后会尝试使用car的Resolver获取解析数据,第一层解析完毕
    • 之后对第一层解析的返回值,进行第二层解析,当前car还包含2个属性需要查询,分别是id、product

      • id在type car中为标量类型,解析结束
      • product在type product类型中为对象类型company,于是Granphql尝试使用companyResolver获取数据,当前field解析完毕
      • 之后对第二层解析的返回值,进行第三层解析,当前product还包含一个属性 name,由于它是标量类型,解析结束。返回数据至前端

    通过上面解析,我们知道了GraphQL大体的解析流程就是遇到一个Query之后,尝试使用它的Resolver取值,之后再其Resolver对返回值进行解析,这个过程是递归的,直到所解析Field的类型是Scalar Type(标量类型)为止。解析的整个过程我们可以把它想象成一个很长的Resolver Chain(解析链)。

    这里对GraphQL的解析过程的解释只是比较粗略的概括,其内部运行机制远比这个复杂,但是对于程序员来说,正常情况下,了解这些就足够你比较清晰的开始开发项目了。

    现在你再把5这部分回过头去同3把部分结合起来看一下,就会发现真正的豁然开朗了。

     

    ---------------看完这篇入门,再看我下一篇就更清晰了:

    用真实项目手把手教你使用SpringBoot集成GraphQL服务,并进行流程开发。

     

     

     

    ---------------有时间再补充一下 mutation 更改,subscription 订阅 这两个Query Root 中的类型入口---------

     

     

     

     

    展开全文
  • 2019独角兽企业重金招聘Python工程师标准>>> ...

    官网

    一、GraphQL简介

    1.GraphQL是什么?

    GraphQL 既是一种用于 API 的查询语言也是一个满足你数据查询的运行时。 GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

    GraphQL的出现不仅仅是针对开发人员的,Facebook在2012年开始在其native mobile apps中使用GraphQL。但有趣的是GraphQL大部分都是在web技术的背景下使用的,并且在native mobile 领域中只得到很少的支持。 Facebook第一次公开谈论GraphQL是在宣布开源计划后不久的2015年React峰会的时候。因为Facebook总是在React的背景下谈GraphQL,所以对于没有React经验的开发人员来说,要理解GraphQL并不是一种仅限于React使用的技术可能还需要一段时间。即便是在这样的背景下诞生的GraphQL依然是一个快速增长的社区 ,事实上GraphQL是一种技术,可以在客户端与API通信的任何地方使用。有趣的是Netflix和Coursera等其他公司都在研究类似的想法以提高API的交互效率。Coursera设想了一种类似的技术,可以让客户指定其数据需求,而Netflix甚至将其解决方案称为Falcor。在GraphQL被开源之后,Coursera完全停止了他们在Falcor上的努力,并转到了GraphQL的学习上。目前已经有很多的公司在使用GraphQL(https://graphql.org/users/
     

    2.GraphQL产生的历史背景

    当提起API设计的时候,大家通常会想到SOAP,RESTful等设计方式,从2000年RESTful的理论被提出的时候,在业界引起了很大反响,因为这种设计理念更易于用户的使用,所以便很快的被大家所接受。我们知道REST是一种从服务器公开数据的流行方式。当REST的概念被提及出来时,客户端应用程序对数据的需求相对简单,而开发的速度并没有达到今天的水平。因此REST对于许多应用程序来说是非常适合的。然而在业务越发复杂,客户对系统的扩展性有了更高的要求时,API环境发生了巨大的变化。特别是从下面三个方面在挑战api设计的方式:

    《1.》移动端用户的爆发式增长需要更高效的数据加载

    Facebook开发GraphQL的最初原因是移动用户的增加、低功耗设备和松散的网络。GraphQL最小化了需要网络传输的数据量,从而极大地改善了在这些条件下运行的应用程序。

    《2.》各种不同的前端框架和平台

    前端框架和平台运行客户端应用程序的异构环境使得我们在构建和维护一个符合所有需求的API变得困难,使用GraphQL每个客户机都可以精确地访问它需要的数据。

    《3.》在不同前端框架,不同平台下想要加快产品快速开发变的越来越难

    持续部署已经成为许多公司的标准,快速的迭代和频繁的产品更新是必不可少的。对于REST api,服务器公开数据的方式常常需要修改,以满足客户端的特定需求和设计更改。这阻碍了快速开发实践和产品迭代。

    3.场景举例

    blog 应用程序中,应用程序需要显示特定用户的文章的 标题,同 一屏幕还显示该 用户所有帖子,以及最后 3 个关注者的名称!

    《1.》使用REST API来现实时,我们通常可以通过访问多次请求来收集数据。

    比如在这个示例中,我们可以通过下面的三步来实现:

     1. 通过 /user/<id>获取初始用户数据

     2. 通过/user/<id>/posts 返回用户的所有帖子

     3. 请求/user/<id>/followers,返回每个用户的关注者列表

    《2.》GraphQL的做法

    二、使用GraphQL

    具体语法可以先看一遍中文官方文档:http://graphql.cn/

    优点:

    1.客户端可以自定义查询语句,通过自定义不仅提高了灵活性,而且服务端只返回客户端所需要的数据,减少网络的开销,提高了性能;

    2.服务端收到客户端的请求,首先做类型检查,及时反馈,而且更加安全;

    3.自动生成文档,降低维护成本;服务端通过新增字段,Deprecated字段,避免版本的繁杂和紊乱。

    具体的语法规则可以参考中文文档,下面,我们通过一个springboot项目的例子,看一下怎么使用它?!

    三、Springboot中使用

    1.配置

    《1.》建立springboot项目

     参考:https://my.oschina.net/hanchao/blog/1803295

     《2.》引入Jar包

    		<!-- graphQL begin-->
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphql-spring-boot-starter</artifactId>
    			<version>5.0.2</version>
    		</dependency>
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphql-java-tools</artifactId>
    			<version>5.2.4</version>
    		</dependency>
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphiql-spring-boot-starter</artifactId>
    			<version>5.0.2</version>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    			<version>1.18.6</version>
    		</dependency>
    		<!-- graphQL end -->
    		<!-- MYSQL&JPA begin -->
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<version>${mysql-connector-java.version}</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-data-jpa</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>com.zaxxer</groupId>
    			<artifactId>HikariCP</artifactId>
    			<version>${hikaricp.version}</version>
    		</dependency>
    		<!-- MYSQL&JPA end -->
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    	<properties>
    		<java.version>1.8</java.version>
    		<mysql-connector-java.version>5.1.46</mysql-connector-java.version>
    		<hikaricp.version>2.4.7</hikaricp.version>
    	</properties>

    注意事项:你的springboot版本不同,引入的GraphQL的版本也会不同!比如:上面我用的是springboot2.x ,我的GraphQL的版本是5.0.2。

                     如果你的springboot项目为1.5.x,那么你可以引用下面的版本GraphQL。

    		<!--graphQL-->
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphql-spring-boot-starter</artifactId>
    			<version>3.10.0</version>
    		</dependency>
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphql-java-tools</artifactId>
    			<version>4.2.0</version>
    		</dependency>
    		<dependency>
    			<groupId>com.graphql-java</groupId>
    			<artifactId>graphiql-spring-boot-starter</artifactId>
    				<version>3.10.0</version>
    		</dependency>

    《3.》建立测试表

    CREATE DATABASE /*!32312 IF NOT EXISTS*/`test` /*!40100 DEFAULT CHARACTER SET utf8 */;
    
    USE `test`;
    
    /*Table structure for table `author` */
    
    DROP TABLE IF EXISTS `author`;
    
    CREATE TABLE `author` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
      `first_name` varchar(50) DEFAULT NULL COMMENT 'firstName',
      `last_name` varchar(50) DEFAULT NULL COMMENT 'lastName',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    /*Table structure for table `book` */
    
    DROP TABLE IF EXISTS `book`;
    
    CREATE TABLE `book` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间',
      `title` varchar(50) DEFAULT NULL COMMENT '标题',
      `author_id` bigint(20) NOT NULL,
      `isbn` varchar(255) DEFAULT NULL,
      `page_count` int(11) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
    /*Table structure for table `user` */
    
    DROP TABLE IF EXISTS `user`;

    《4.》建立Entity

    BaseEntity.java

    package com.hanchao.graphql.graphql.entity;
    
    import lombok.Data;
    
    import javax.persistence.*;
    import java.io.Serializable;
    import java.util.Date;
    
    /**
     * @author hanliwei
     * @create 2019-02-12 17:18
     */
    @Data
    @MappedSuperclass
    public class BaseEntity implements Serializable {
    
    
        /** ID */
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(columnDefinition = "bigint", nullable = false)
        protected Long id;
    
        /** 创建时间戳 (单位:秒) */
        @Column(nullable = false, updatable = false)
        @Temporal(TemporalType.TIMESTAMP)
        protected Date createdTime;
    
        /** 更新时间戳 (单位:秒) */
        @Column(nullable = false)
        @Temporal(TemporalType.TIMESTAMP)
        protected Date updatedTime;
    
    
        public BaseEntity() {
            createdTime = new Date();
            updatedTime = createdTime;
        }
    
        @PreUpdate
        private void doPreUpdate() {
            updatedTime = new Date();
        }
    
    }
    

    Author.java

    package com.hanchao.graphql.graphql.entity;
    
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    
    
    @Entity
    @Data
    @EqualsAndHashCode(callSuper = false)
    public class Author extends BaseEntity {
    
        @Column(columnDefinition = "varchar(50)")
        private String firstName;
    
        @Column(columnDefinition = "varchar(50)")
        private String lastName;
    }
    

    Book.java

    package com.hanchao.graphql.graphql.entity;
    
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    
    import javax.persistence.Column;
    import javax.persistence.Entity;
    
    
    @Entity
    @Data
    @EqualsAndHashCode(callSuper = false)
    public class Book extends BaseEntity {
        @Column(columnDefinition = "varchar(50)")
        private String title;
    
        private String isbn;
    
        private int pageCount;
    
        private long authorId;
    }
    

    BookInput.java 参数实体(后面会用到)

    package com.hanchao.graphql.graphql.model;
    
    import lombok.Data;
    
    
    @Data
    public class BookInput {
        private String title;
    
        private String isbn;
    
        private int pageCount;
    
        private long authorId;
    }
    

    《5.》mysql配置

    application.properties

    ############ mysql-开始 ###########
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.show-sql=true
    
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useServerPrepStmts=true&jdbcCompliantTruncation=false
    jdbc.username=root
    jdbc.password=root
    ############ mysql-结束 ###########

    DataSourceConfig.java

    package com.hanchao.graphql.graphql.config.mysql;
    
    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.env.Environment;
    
    import javax.sql.DataSource;
    
    /**
     * @author hanliwei
     * @create 2019-01-15 15:07
     */
    @Configuration
    public class DataSourceConfig {
        @Autowired
        private Environment env;
    
        @Bean
        public DataSource dataSource() {
            HikariConfig config = new HikariConfig();
            config.setDriverClassName(env.getProperty("jdbc.driver"));
            config.setJdbcUrl(env.getProperty("jdbc.url"));
            config.setUsername(env.getProperty("jdbc.username"));
            config.setPassword(env.getProperty("jdbc.password"));
            /**
             * 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count)
             */
            config.setMaximumPoolSize(20);
            config.setMinimumIdle(5);
            /**
             * 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒
             */
            config.setConnectionTimeout(5 * 1000L);
            /**
             * 一个连接idle状态的最大时长(毫秒),超时则被释放(retired),缺省:10分钟
             */
            config.setIdleTimeout(3 * 60 * 1000L);
            config.addDataSourceProperty("cachePrepStmts", "true");
            config.addDataSourceProperty("prepStmtCacheSize", "250");
            config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
            config.setConnectionTestQuery("SELECT 1");
            config.setPoolName("beehive_statistic");
            return new HikariDataSource(config);
        }
    }
    

    《6.》Repo

    AuthorRepo.java

    package com.hanchao.graphql.graphql.repo;
    
    
    import com.hanchao.graphql.graphql.entity.Author;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    
    /**
     * @author hanliwei
     * @create 2019-02-12 17:29
     */
    public interface AuthorRepo extends JpaRepository<Author,Long> {
    
        Author findAuthorById(Long id);
    }
    

    BookRepo.java

    package com.hanchao.graphql.graphql.repo;
    
    import com.hanchao.graphql.graphql.entity.Book;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import java.util.List;
    
    /**
     * @author hanliwei
     * @create 2019-02-12 17:31
     */
    public interface BookRepo extends JpaRepository<Book,Long> {
        List<Book> findByAuthorId(Long id);
    
        Book findBookById(Long id);
    }
    

    《7.》Resolver

    AuthorResolver.java  用于解析字段

    package com.hanchao.graphql.graphql.resolver;
    
    import com.coxautodev.graphql.tools.GraphQLResolver;
    import com.hanchao.graphql.graphql.entity.Author;
    import com.hanchao.graphql.graphql.entity.Book;
    import com.hanchao.graphql.graphql.repo.BookRepo;
    import lombok.AllArgsConstructor;
    import org.springframework.stereotype.Component;
    
    import java.text.SimpleDateFormat;
    import java.util.List;
    
    /**
     * @author hanliwei
     * @create 2019-02-12 17:33
     */
    @Component
    @AllArgsConstructor
    public class AuthorResolver implements GraphQLResolver<Author> {
    
        private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        private BookRepo bookRepo;
    
        public String getCreatedTime(Author author) {
            return sdf.format(author.getCreatedTime());
        }
    
        public List<Book> getBooks(Author author) {
            return bookRepo.findByAuthorId(author.getId());
        }
    }
    

    BookResolver.java

    package com.hanchao.graphql.graphql.resolver;
    
    import com.coxautodev.graphql.tools.GraphQLResolver;
    import com.hanchao.graphql.graphql.entity.Author;
    import com.hanchao.graphql.graphql.entity.Book;
    import com.hanchao.graphql.graphql.repo.AuthorRepo;
    import lombok.AllArgsConstructor;
    import org.springframework.stereotype.Component;
    
    
    /**
     * @author hanliwei
     * @create 2019-02-12 17:36
     */
    @Component
    @AllArgsConstructor
    public class BookResolver implements GraphQLResolver<Book> {
    
        private AuthorRepo authorRepo;
    
        public Author getAuthor(Book book) {
            return authorRepo.findAuthorById(book.getAuthorId());
        }
    }
    

    Mutation.java 

    和root.graphqls文件的方法对应,用于更新操作

    package com.hanchao.graphql.graphql.resolver;
    
    import com.coxautodev.graphql.tools.GraphQLMutationResolver;
    import com.hanchao.graphql.graphql.entity.Author;
    import com.hanchao.graphql.graphql.entity.Book;
    import com.hanchao.graphql.graphql.model.BookInput;
    import com.hanchao.graphql.graphql.repo.AuthorRepo;
    import com.hanchao.graphql.graphql.repo.BookRepo;
    import lombok.AllArgsConstructor;
    import org.springframework.stereotype.Component;
    
    /**
     * @author hanliwei
     * @create 2019-02-12 18:58
     */
    @Component
    @AllArgsConstructor
    public class Mutation implements GraphQLMutationResolver {
        private AuthorRepo authorRepo;
        private BookRepo bookRepo;
    
        public Author newAuthor(String firstName, String lastName) {
            Author author = new Author();
            author.setFirstName(firstName);
            author.setLastName(lastName);
            return authorRepo.save(author);
        }
    
        public Book newBook(String title, String isbn, int pageCount, Long authorId) {
            Book book = new Book();
            book.setTitle(title);
            book.setIsbn(isbn);
            book.setPageCount(pageCount);
            book.setAuthorId(authorId);
            return bookRepo.save(book);
        }
    
    
        public Book saveBook(BookInput input) {
            Book book = new Book();
            book.setTitle(input.getTitle());
            book.setIsbn(input.getIsbn());
            book.setPageCount(input.getPageCount());
            book.setAuthorId(input.getAuthorId());
            return bookRepo.save(book);
        }
    
        public Boolean deleteBook(Long id) {
            bookRepo.deleteById(id);
            return true;
        }
    
        public Book updateBookPageCount(int pageCount,long id) {
            Book book = bookRepo.findBookById(id);
            book.setPageCount(pageCount);
            return bookRepo.save(book);
        }
    
    }
    

    Query.java

    和root.graphqls中的方法对应!用于查询操作

    package com.hanchao.graphql.graphql.resolver;
    
    import com.coxautodev.graphql.tools.GraphQLQueryResolver;
    import com.hanchao.graphql.graphql.entity.Author;
    import com.hanchao.graphql.graphql.entity.Book;
    import com.hanchao.graphql.graphql.repo.AuthorRepo;
    import com.hanchao.graphql.graphql.repo.BookRepo;
    import lombok.AllArgsConstructor;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * @author hanliwei
     * @create 2019-02-12 18:00
     */
    @Component
    @AllArgsConstructor
    public class Query implements GraphQLQueryResolver {
    
        private AuthorRepo authorRepo;
    
        private BookRepo bookRepo;
    
        public Author findAuthorById(Long id) {
            return authorRepo.findAuthorById(id);
        }
    
        public List<Author> findAllAuthors() {
            return authorRepo.findAll();
        }
    
        public Long countAuthors() {
            return authorRepo.count();
        }
    
        public List<Book> findAllBooks() {
            return bookRepo.findAll();
        }
    
        public Long countBooks() {
            return bookRepo.count();
        }
    }
    

    《8.》建立graphql文件

    在resource下面建立一个文件夹graphql ,该文件夹下面必须有两个文件,

    名字分别为:root.graphqls

                        schema.graphqls

    root.graphqls

    #查询入口
    type Query {
        ####### Author相关 #########
        findAuthorById(id:Long!): Author
    
        findAllAuthors: [Author]!
    
        countAuthors: Long!
    
        ####### Book相关 #########
        findAllBooks: [Book]!
        countBooks: Long!
    }
    
    type Mutation {
        newAuthor(firstName: String!,lastName: String!) : Author!
    
        newBook(title: String!,isbn: String!,pageCount: Int, authorId: Long!) : Book!
        saveBook(input: BookInput!) : Book!
        deleteBook(id: ID!) : Boolean
        updateBookPageCount(pageCount: Int!, id:Long!) : Book!
    }

    schema.graphqls

    注意:我们需要给每个字段加上注释,要不使用者会很崩溃!

    type Author {
        #作者Id
        id: Long!
        #创建时间
        createdTime: String
        #名
        firstName: String
        #姓
        lastName: String
        #该作者的所有书籍
        books: [Book]
    }
    
    input BookInput {
        title: String!
        isbn: String!
        pageCount: Int
        authorId: Long
    }
    
    type Book {
        id: Long!
        title: String!
        isbn: String!
        pageCount: Int
        author: Author
    }

    加了注释后的效果:

    c765acbaf69d1575f4b4d45378f627bf62c.jpg

    注意事项:如果都放在schema.graphqls中也可以,可以类似这样

    9e7aa17f7666023acc62a04764a65dddf71.jpg

     

    《9.》测试

    到此,项目应该建立好了,可以启动了,访问:http://localhost:8080/graphiql ,可以测试一下root.graphqls中的方法了!

    具体测试过程,我们放在下一篇文章中吧,要不感觉一篇博文太长了!

    源码:https://github.com/windhan2100/graphql

    转载于:https://my.oschina.net/hanchao/blog/3014079

    展开全文
  • GraphQL入门:非常简单

    千次阅读 2019-07-30 15:26:52
    现在越来越多的公司开始用GraphQL来建立API。我们获取数据的方式发生了革命性的变化。 GraphQL的起源以及我们为什么要使用这种方法 GraphQL来自于facebook。facebook内部正在寻找一种让他们的新闻推送在移动平台上...

    前端技术小哥

    原文:https://medium.com/free-code-camp/give-it-a-…
    译者:前端技术小哥

    现在越来越多的公司开始用GraphQL来建立API。我们获取数据的方式发生了革命性的变化。
    GraphQL的起源以及我们为什么要使用这种方法
    GraphQL来自于facebook。facebook内部正在寻找一种让他们的新闻推送在移动平台上加载的更稳定的方法。

    使用传统REST API结构,新闻推送对多个API端点进行了多重调用,以便获得所需的所有数据。但是这一路走来,API调用也会取到新闻推送那部分不需要的多余数据。不仅如此,在收到数据之后,前端工程师还必须通过数据解析找到他们想要的片段。
    facebook工程师想知道,“如果我们可以编写指令语言,那么我们可以在单个API请求中找到需要的所有信息?”

    GraphSQL是工程师们努力的研究结果。首先它将数据库中对象之间的关系映射到我们创建的一个图表中。然后他们设计了一个指令语言来找到这些关系的映射。因此,它被命名为“GraphSQL”。
    通过添加查询语言,GraphSQL API现在可以接受单个端点的所有接收请求。然后,他们取回并返回所请求的数据,并且只返回到所请求的数据。这样将不再重复获取用户不使用的信息。

    是一份规范而不是实现方法
    最重要的是,Facebook决定开放源代码GraphQL作为一份规范。
    这意味着它可以通过任何编程语言实现。只要实现以规定的方式分析指令得出它的框架,它就能很好地运行任何其他GraphQL应用程序。
    事实上,现在每种主流编程语言中都有几十种实现GraphQL的方法。
    在本文中,我们将使用JavaScript编写的GraphQL来实现,这是在任何语言中都适用的相同基本原则。您可以查看GraphQL实现的完整列表,以找到您最喜欢的语言。

    基础架构

    开发一个功能良好的GraphQL API需要两个部分:服务器和客户端。服务器处理传入的指令,解析这些指令,使用规范来获取数据,并通过JSON进行返回。
    客户端使您的应用程序能够与服务器通信。虽然您只需向GraphQL端点发送简单的POST请求,但是如果使用GraphQL客户端来帮助发送指令,则可以获得更好的使用效果。
    构建GraphQL API可能比构建REST API更加的集中。然而,在速度和实际应用上的优点可以弥补它在复杂或高强度的应用程序运行的不足。

    GraphQL是什么样的

    我们API的目标是发送一个GraphQL指令并收到一个响应。我们一起来看看GraphQL大概是什么样的。
    记住,GraphQL是它自己的语言。它并不是一种很难学习的语言,而且在很大程度上编写指令是很直接的。让我们想象一下,我们有一个包含了飞机和乘客信息的数据库。在GraphQL中,我们可以这样定义飞机:

    {
      flight(id: "1234") {
        origin
        destination
      }
    }
    

    这是GraphQL所发出的:“给我飞机1234的始发地和目的地。”我们会收到这样的回应:

    {
      "data": {
        "flight": {
          "origin": "DFW",
          "destination": "MKE"
        }
      }
    }
    

    提示:
    我们收到正好都是我们指令所要求的内容,不多也不少。
    我们还收到了与我们发送的原始指令完全相同的格式响应。这些是GraphQL API的标记。这是GraphQL如此快速和强大的原因。
    不过,这并不是我们所能做的事情。我们想要得到这架飞机上的乘客信息,该怎么做:

    {
      flight(id: "1234") {
        origin
        destination
        passengers {
          name
        }
      }
    }
    

    现在,GraphQL将向下扫描该航班与乘客之间的关系图。我们会得到一份乘客名单反馈:
    现在我们可以用一个API调用立即查看此航班上的所有乘客。
    为什么小小小黄正在飞往国内,由于GraphSQL将数据分析为图形,我们也可以在其他方向进行扫描。

    {
      "data": {
        "flight": {
          "origin": "DFW",
          "destination": "MKE",
          "passengers": [
            {
              "name": "小明"
            },
            {
              "name": "小黄"
            },
            {
              "name": "小小"
            }
          ]
        }
      }
    }
    

    现在我们可以看到飞机Luke记录了什么:

    {
      "data": {
        "person": {
          "passport_number": 78120935,
          "flights": [
            {
              "id": "1234",
              "date": "2019-05-24",
              "origin": "DFW",
              "destination": "MKE"
            },
            {
              "id": "2621",
              "date": "2019-07-05",
              "origin": "MKE",
              "destination": "DFW"
            }
          ]
        }
      }
    }
    

    哇,他要在密尔沃基呆一个多月了!我想知道他在那里干什么?
    列一项清单

    • 所以我们创建GraphQL API需要什么?
    • 选择一个框架来实现GraphQL的服务器。我们会使用Express。
    • 定义架构,以便GraphQL知道如何引导输入的指令。
    • 构造函数来处理指令,和告诉GraphQL返回到哪里。
    • 构造端点。
    • 编写获取数据的客户端指令。
    • 然后,您可以使用GraphQL指令来向客户端应用程序供电。本教程不会进入前端使用GraphSQL所有不同方式,但它与所有前端框架相兼容。

    最终,GraphQL的大多数使用将涉及到与数据库的对话。在本教程中,我们将不讨论向Express添加数据库和允许GraphQL查询和更新该数据库。这是一个完全不同的教程。

    1.实现服务器

    首先,需要为API奠定基础。您将需要安装的Nodejs和NPM随本教程从此处退出。

    构建一个Barbones Express服务器。启动NPM:

    $ npm init
    This utility will walk you through creating a package.json file.
    It only covers the most common items, and tries to guess sensible defaults.
    See `npm help json` for definitive documentation on these fields
    and exactly what they do.
    Use `npm install <pkg>` afterwards to install a package and
    save it as a dependency in the package.json file.
    Press ^C at any time to quit.
    package name: (graphql-medium) 
    version: (1.0.0) 
    description: 
    entry point: (index.js) 
    test command: 
    git repository: 
    keywords: 
    author: 
    license: (ISC) 
    About to write to /home/bennett/Repos/graphql-medium/package.json:
    {
      "name": "graphql-medium",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "",
      "license": "ISC"
    }
    Is this OK? (yes)
    

    只需点击Enter就可以跳过初始化过程。也可以回头编辑Package.json。接下来,让我们安装Express、GraphQL和Express-GraphQL库:

    $ npm install express express-graphql graphql
    npm notice created a lockfile as package-lock.json. You should commit this file.
    npm WARN graphql-medium@1.0.0 No description
    npm WARN graphql-medium@1.0.0 No repository field.
    + express-graphql@0.8.0
    + graphql@14.3.1
    + express@4.17.0
    added 53 packages from 38 contributors and audited 151 packages in 6.169s
    found 0 vulnerabilities
    

    现在,我们将创建一个名为index.js的新文件,并在那里创建一个新的barebones Express服务器:

    // index.js
    const express = require('express');
    const app = express();
    app.get('/', function(req, res) {
      res.send('Express is working!')
    });
    app.listen(4000, function() {
      console.log('Listening on port 4000')
    });
    

    尝试运行节点index.js。您应该会看到一条“Listening on port 4000”的消息,如果您访问http://localhost:4000/,您将看到“Express正在工作”

    添加GraphQL&DefineSchema我们已经安装了GraphQLNPM包。现在可以运行这个程序了。

    首先,我们需要导入必要的模块:

    const graphqlHTTP = require('express-graphql');
    const { buildSchema } = require('graphql');
    

    2.接下来,我们将使用这些模块。

    让我们开始定义GraphQL API的结构。输入的指令应该是什么样的?现在,让我们定义一个HelloWorld模式,以获取工作的事物:

    let schema = buildSchema(`
      type Query {
        hello: String
      }
    `);
    

    这个简单的模式让GraphQL知道,当有人发送查询“Hello”时,我们将返回一个字符串。注意里面的那些小后背 ` 。这表明我们使用的是JavaScript模板文字。基本上,我们使用这些回退来告诉JavaScript,我们将要用另一种语言编写GraphQL指令的语言。

    3.解决指令

    因此,当有人为Hello提交指令时,我们知道我们将返回一个字符串。这是在我们的模式中定义的。现在,我们需要确切地告诉GraphQL它应该返回什么字符串。根据输入的指令确定要返回的数据是GraphQL中“解析器”的工作。在本例中,分析方法很简单。我们将返回字符串“Hello world”。

    return 'Hello world!';
    

    但是,我们需要将该返回语句放在一个可以多次调用的函数中,无论何时有人在hello中编写指令:

    function() {
      return 'Hello world!';
    }
    

    现在,hello可能不是我们实现的唯一指令的类型。未来,我们可能还包括其他功能的“端点”。因此,我们应该确保我们刚刚创建的这个函数映射到hello与我们API的所有其他部分一起,保存在一个对象中。

    let root = {
      hello: function() {
        return 'Hello world!';
      },
    }
    

    这是一个调用对象的公约,它可以保留所有解析器的原始数据,无论您需要什么,都可以通过它得到。

    4.设置一个客户端

    精明的读者会注意到,我们在步骤2中导入了GraphqlHTTP,但我们还没有使用它。现在是时候了。我们现在已经为GraphSQLServer提供了一切。我们只需要通过URL端点提供它。在Express中,我们将创建一个新的路由来服务GraphSQLAPI

    app.use('/graphql', graphqlHTTP({
      schema: schema,
      rootValue: root,
      graphiql: true,
    }));
    

    模式和原始数据指向步骤2和3中定义的变量。GraphSQL是一个有用的可视化工具,它与GraphSQL一起安装。正如我们将在第二个中看到的,它很容易得出您的API是如何工作的。这是我们的GraphSQLServer源代码的最终状态。
    运行程序&输入一个指令
    准备测试该程序!
    npm index.js启动应用程序
    转到http://localhost:4000/GraphSQL您应该看到GraphSQL界面:

    我们现在可以使用这个接口来确保我们的API正在工作!
    让我们写一个指令。这个很简单的。我们总是在大括号中写我们的GraphSQL指令。然后,我们指定要提取的任何属性所对应的模式对象。
    在这种情况下,目前我们API中只有一个东西要提取:

    {
      hello
    }
    

    如果你点击了提交按钮,你将会看到:

    {
      "data": {
        "hello": "Hello world!"
      }
    }
    

    它正在工作!
    添加更多的端点
    向API添加端点就像在模式中定义新字段一样简单,然后将解析器函数添加到原始数据中。
    您也可以让指令逐渐变得复杂。下一步,我推荐这个来自官方文档的引导building a dice rolling API

    GraphQL, FTW

    GraphQL非常好用,它在应用中迅速成长。在未来的几年里,它有可能成为一种无处不在的API技术。希望本文为您很好地介绍了可以在项目中使用GraphQL的原因和方法。

    ❤️ 看之后

    • 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
    • 关注公众号「新前端社区」,号享受文章首发体验!每周重点攻克一个前端技术难点。

    展开全文
  • GraphQL简介及入门

    2019-09-02 15:10:06
    1、什么是GraphQL? GraphQL 是由 Facebook 创造的用于描述复杂数据模型的一种查询语言。这里查询语言所指的并不是常规意义上的类似 sql 语句的查询语言,而是一种用于前后端数据查询方式的规范。官网(中文):...
  • 认识GraphQL

    2020-11-24 20:48:46
    GraphQL 这种方式能够将原有 RESTful 风格时的多次请求聚合成一次请求,可以定制查询结果(结构) 和REST API的区别: REST 1、返回的数据格式和类型都是定义好的,如果想前后端保持预期效果, 1)协调后端改接口...
  • GraphQL

    2020-05-01 13:48:34
    GraphQL 一种用于 API 的查询语言 诞生原因 遭遇问题 兼容多平台导致字段冗余 一个页面需要多次调用 API 聚合数据 需求经常改动导致接口很难为单一接口精简逻辑 解决思路 把静态的接口变成动态 使用例子 安装包 ...
  • GraphQL 入门详解

    2019-07-31 08:00:00
    由于微信外链限制,推荐阅读等链接无法点击,可点击阅读原文跳转至原文,查看外链。作者:MudOnTiregithub 地址:https://github.com/MudOn...
  • GraphQL 学习笔记

    千次阅读 2019-06-21 17:15:32
    GraphQL 是由 Facebook 创造的用于描述复杂数据模型的一种查询语言。这里查询语言所指的并不是常规意义上 的类似 sql 语句的查询语言,而是一种用于前后端数据查询方式的规范。 官网(中文):https://graphql.cn/ ...
  • 我经常听到的 GraphQL 到底是什么? 当听说出了一门新技术的时候,你可能会和我一样有以下 3 种反应: 1. 嫌弃 又来一个 JavaScript 类库?反正我只用 JQuery 就行了。 2. 感兴趣 嗯,也许我应该去了解一下...
  • Graphql是什么

    千次阅读 2018-06-26 10:19:04
    阅读目录GraphQL是什么.net下如何运用GraphQL运用GraphQL调用Github api结语 一、Graphql是什么 最近在折腾使用Github api做个微信小程序练练手,本篇文章就是在这个过程中记录。 直接先看下GraphQL的语法风格,...
  • 本文中搭建的项目是在参考company的真实项目基础上简化而来,主要简化了许多业务逻辑、一些数据源(比如OTS redis在项目中的使用以及web权限验证等等,,),项目变得简单清晰,突出使用SpringBoot集成GraphQL服务,...
  • Nodejs自从2009年被开发出来以后,至今已经走过了9个年头,目前最新的稳定版已经到了10.13。从问世以后,Nodejs就深受前端工程师的喜欢。在携程内部,Nodejs也是应用广泛,从开发工具到web...一、前后端分离在机票...
  • GraphQL访问安全分析

    千次阅读 2019-03-25 14:46:03
    本文以GraphQL中一些容易让初学者与典型Web API(为了便于理解,下文以目前流行的RESTful API为例代指)混淆或错误理解的概念特性进行内容划分,由我从安全的角度抛出GraphQL应该注意的几点安全问题,而@图南则会更...
  • GraphQL 初探

    千次阅读 2018-01-02 16:52:37
    转自http://taobaofed.org/blog/2015/11/26/graphql-basics-server-implementation/ 0.问题来了 DT 时代,各种业务依赖强大的基础数据平台快速生长,如何高效地为各种业务提供数据支持,是所有人关心的问题。 ...
  • 最新GraphQL基础教程

    千次阅读 2018-12-10 22:39:03
    文章目录1 GraphQL简介2 查询和变更2.1 字段2.2 参数2.3 别名2.4 片段2.5 操作名称2.6 变量2.7 指令2.8 变更2.9 内联片段2.10 元字段3 Schema和类型3.1 类型系统3.2 类型语言3.3 对象类型和字段3.4 参数3.5 查询和...
  • angular4前后端分离Apollo Client is the flexible, community-driven GraphQL client for Angular, JavaScript, and native platforms. It is designed from the ground up to make it easy to build UI components...
  • 首先composer安装扩展包:folklore/graphql(github-most stars),然后最好是不管laravel哪个版本先去...Folklore\GraphQL\ServiceProvider::class, 然后再去publish配置 php artisan vendor:publish --prov...
  • graphql

    2020-04-02 21:29:47
    GraphQL 是一种用于 API 的查询语言 特点: 请求 需要的数据 即可,不多不少 例如:account中有name, age, sex等,可以只取得需要的字段name 获取多个资源时,只用一个请求即可 描述所有可能类型的系统,便于维护。...
  • GraphQL 极速入门

    2019-06-21 20:16:47
    ​ 之前在自己的Blog项目 Blog++ 中使用了GraphQL,发现确实不错,所以写一篇关于GraphQL的入门文章。 What is GraphQL? ​ GraphQL是一种API查询语言,对API中的数据提供一套描述,使客户端能够准确地获得需要地...
  • facebook GraphQL

    千次阅读 2016-09-15 16:21:27
    graphql
  • graphql_GraphQL简介

    2020-08-30 11:53:38
    graphqlWhat is GraphQL 什么是GraphQL How it works 这个怎么运作 GraphQL Queries GraphQL查询 Fields and arguments 字段和参数 Aliases 别名 Fragments 碎片 GraphQL Queries GraphQL查询 GraphQL ...
  • The Anatomy of a GraphQL Query《GraphQL 名词 101:解析 GraphQL 的查询语法》【译】 GraphQL 日渐成为数据查询的主流标准之一,整个生态圈也蓬勃发展。本文则由浅入深地详细介绍基础的 GraphQL 格式与关键字,有...
  • GraphQL实战-GraphQL介绍 GraphQL的前世今生 Facebook的业务线有移动端,PC和其它,不同的场景下对一个资源所需要的信息是不同的。如移动端需要User的a、b、c三个字段,PC需要b、c、d三个字段;对于此场景,...
  • graphql详解

    千次阅读 2018-08-12 11:10:20
    随着系统业务量的增大不同的应用和系统共同使用着许多的服务api,而随着业务的变化和发展,不同的应用对相同资源的不同使用方法最终会导致需要维护的服务api数量呈现爆炸式的增长,比如我试着跑了下我们自己业务里的...
  • GraphQL what & why GraphQL是一种API查询语言,它是一种动态的API查询方式,同一个API可以返回不同的结果。 普通的REST请求是预先定义好的,前端需要什么数据会和后端讲,然后后端开始商量好返回数据格式,...
  • GraphQL实战

    2020-02-13 16:56:34
    GraphQL实战 1.1.根据需求编写GraphQL文件 在resources目录下新建.graphqls文件 schema { query: HaokeQuery } type HaokeQuery { HouseResources(id:Long) : HouseResources HouseResourcesList(page:Int, ...

空空如也

1 2 3 4 5 ... 20
收藏数 11,524
精华内容 4,609
关键字:

graphql