小洋的前端记事本(NO.5):GraphQL 小实践

这里记录着小洋童鞋在前端道路上的所思所想所感。

生命在于折腾,不在折腾中崩溃,就在折腾中涅槃。



GraphQL| A query language for your API,诞生于移动快速迭代时代,理念是给客户端提供类似”数据库操作“的能力,使其能够从服务接口这个大的“大数据库”中去筛选或修改想要的数据,用以满足前端数据的个性化需求,既保证了多样性,又控制了接口数量,期望更好的分类维护接口。GraphQL 服务器有很多的实现,Node.js 当然也不例外,下面我们就一起折腾折腾 GraphQL 吧 : )

GraphQL Node.js Server

准备

首先给大家简单的介绍一下 GraphQL 特性,GraphQL 其实是一种和后端交换数据的协议,像通过 SQL 语言可以操作数据库一样,GraphQL 通过 Schema 的方式来控制数据,并约定提供以下能力:

  • query 查询数据 详见
  • mutation 特殊查询,可以看做为修改数据 详见
  • introspection 检查数据的支持情况(下文简称”自省”) 详见

可以说很多数据交换的情景都可以使用 GraphQL 的概念,我们就先基于 Express 来快速打造一个 http 服务器 ( GraphQL 官方并没有说只限于 http) 用最简单的方式体验一下 GraphQL,我们的项目起始于 package.json:

{
  "name": "graphql-start",
  "description": "简单体验一下 GraphQL ",
  "private": true,
  "dependencies": {
    "babel-register": "^6.18.0", // ES6 你懂的
    "babel-preset-es2015": "^6.18.0", // ES6 你懂的
    "express": "^4.14.0", // 快速搭一个服务器
    "body-parser": "^1.15.2", // Express 中间件,获取 GraphQL 请求用的
    "graphql": "^0.8.2" // 我们的主角,处理 GraphQL 请求用的
  }
}

$npm install 以后我们就可以愉快的玩耍了,首先搭好程序入口,本地服务以及处理 GraphQL 请求的 handler:

/ 入口:index.js /
require('babel-register')({
    presets: [ 'es2015' ]
});
require('./server.js');

/ 本地服务:server.js /
// base
import express from 'express';
import {graphql} from 'graphql';
import bodyParser from 'body-parser';

// 组织服务端识别的 schema
import schema from './schema';

let app = express();
let PORT = 3000;

// 用 text 的方式解析 request doby,形象起见我们设置一个特殊的 Content-Type
app.use(bodyParser.text({type: 'application/graphql'}));

// 创建 "/graphql" 的路由(可以看做一个API)接收 GraphQL 请求,使用 GET ? 特殊字符,url链接长度不考虑啦?
app.post('/graphql', (req, res) => {
    // 组织服务端识别的 schema, 并处理 GraphQL 请求
    graphql(schema, req.body).then((result) => {
        res.send(JSON.stringify(result, null, 2));
    })
});

let server = app.listen(PORT, function () {
    let host = server.address().address;
    let port = server.address().port;

    console.log('GraphQL listening at http://127.0.0.1', host, port);
});

/ schema 实例:schema.js /
import {GraphQLSchema, GraphQLInt, GraphQLString, GraphQLBoolean, GraphQLObjectType, GraphQLList} from 'graphql';

// 创建一个GraphQLSchema实例,它提供了一个配置,下面主要来写我们的顶级键 query 和 mutation
let schema = new GraphQLSchema({
  query: ... ,
  mutation: ...
});

export default schema;

上述代码创建了入口 index.js 、本地服务 server.js 以及 schema 实例 schema.js ,至此我们的 GraphQL 服务的骨架就出来了,下面我们只要来把 schema.js 填充完整就可以体验 GraphQL 啦~

GraphQL Schema

以下所有代码均在 schema.js 中,开始前可以先了解一下 graphql-js

我们可以按照上述描述数据筛选数据然后获取数据的顺序生成测试代码感受一下 GraphQL 的魅力吧~

目标数据

首先我们先创建一个模拟数据源 todo list:

let TODOs = [
    {
        "id": 111,
        "title": "Read emails",
        "completed": false
    },
    {
        "id": 222,
        "title": "Buy orange",
        "completed": true
    }
];

todo list 是一个元素为 todo object 的数组,由于我们要给客户端提供筛选及自省的能力,所以我们要通过 GraphQL 的 type 来描述所有组织结构,首先我们来描述元素 todo object

// 申明 todo object 的结构及组成
let TodoType = new GraphQLObjectType({
    name: 'todo',
    fields: function () {
        return {
            id: {
                type: GraphQLInt, // Int 型
                description: "todo object's id"
            },
            title: {
                type: GraphQLString, // String 型
                description: "todo object's title"
            },
            completed: {
                type: GraphQLBoolean, // Boolean 值
                description: "todo object's status"
            }
        }
    }
});

那么 todo list 显而易见就是 new GraphQLList(TodoType) 啦,即元素为 TodoType 的数组。

Schema

有了数据源我们就可以愉快的通过 GraphQL Schema 来取数据啦,我们的第一个 Schema 就先为获取整个 todo list 吧,先在服务器上注册一个处理 GraphQL 请求的方法:

let schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'getTODOs', // query name
        description: 'The TODOs!',
        fields: function () {
            return {
                TODOs: { // 返回结果 {TODOs: [...]}
                    type: new GraphQLList(TodoType),
                    description: 'The TODOs List!',
                    resolve: function () {
                        // 返回 todo list
                        return TODOs;
                    }
                }
            }
        }
    })
    //, mutation: ...
});

之前已经创建了一个端口为3000,路由为 /graphql 的 GraphQL API,至此终于可以 $node index.js 开始我们的 GraphQL 之旅啦~

query

比如我们可以通过下面的 query 来获取 TODOs 下的 title ,由于 TODOs 是数组所以结果对应也是数组:

注:通过控制台,Postman等均可构造 POST 请求验证结果,本文所有请求都通过 curl 来构造(包括添加 ContentType 请求头及 data)。

introspection

在介绍自省之前,我们可以先了解一下域,每个 GraphQL 根域都有 __schema 域,这个域有一个子域叫 queryType。我们可以通过查询这些域来了解 GraphQL 服务器支持那些查询,按照 Object 的方式理解其实就是通过对象的属性可以了解其结构及内容

看到上面的结果大家应该都有感觉了,其实 GraphQL 请求的成功与否与接口的 Schema 设计结构息息相关,所有的数据结构查询都是按协商好的规则进行的,还记得我们声明的 GraphQLSchema 是怎么写的吗?

{
    name: 'getTODOs',
    description: 'The TODOs!',
    fields: function () {
        return {
            TODOs: {
                type: new GraphQLList(TodoType),
                description: 'The TODOs List!'
                ...
            }
        }
    }
}

发现结果是一一对应的了吧,哈哈~ 可以记住这个“万能查询”,同时 GraphQL 也提供了__Type, __TypeKind, __Field, __InputValue等等的关键字来检测接口所支持查询的属性,比如我们现在已经知道了“/garphql”这个 API 会返回一个对象数组,那我想知道他包含的对象是什么结构咋变咧?

没错我们可以通过限定 __Type 去查已经注册的 "todo" 对象来自省接口,还有很多关键字方法都可以达到上述的效果本文就不一一列举了。细心的同学发现上面 __type(name: todo) 的用法了吧,同样也可以用到我们的代码中来查询具体某一条的 todo 对象:

let schema = new GraphQLSchema({
    query: new GraphQLObjectType({
        name: 'getTODO',
        fields: function () {
            return {
                todo: {
                    type: TodoType, // 这次我们只返回一个 todo 对象
                    description: 'The TODO!',
                    args: {
                        id: {
                            type: GraphQLInt,
                            required: true
                        }
                    },
                    resolve: function (root, param, context) {
                        console.log(param.id);
                        // 这里就可以放和数据库的交互了,本例就简单的返回第一个 todo 对象
                        return TODOs[0];
                    }
                }
            }
        }
    })
    //, mutation: ...
});

mutation

有查询数据当然也有修改数据,mutation 查询和普通查询请求(query)的重要区别在于 mutation 操作是按照顺序执行的,即多次修改数据后再查询,其返回一定是最后一次修改后的结果:

let schema = new GraphQLSchema({
    // query: ... ,
    mutation: new GraphQLObjectType({
        name: 'updateTODO',
        fields: {
            updateTODO: {
                type: TodoType,
                description: 'Update the TODO status',
                args: {
                    id: {
                        type: GraphQLInt,
                        required: true
                    }
                },
                resolve: function (root, param, context) {
                   // 这里就可以放和数据库的交互了,本例就简单的更新 completed 属性
                    console.log(param.id);
                    TODOs[0].completed = true;
                    return TODOs[0];
                }
            }
        }
    })
});

调试

GraphiQL 可以快速联想搜索接口,并提供了非常强大的自省能力,是很好的提高 GraphQL 开发效率的工具

总结

正如官方所说 GraphQL 是一个查询语言,而且目前还未完成,未来也可能会有更多更大的变动,但已经拓宽了我们的思路,让我们看到了更多的可能性。目前 GraphQL 利好主要是在于前端的开发效率,落地时需要服务端的全力配合,也存在着一定的安全风险(暴力破解接口能力等),最大的性能瓶颈可能来自于数据库查询,但究竟好不好用,关键是看你怎么用了,对吧? 我们仍在前行,阳光总在风雨后。

时间: 2024-10-24 02:25:45

小洋的前端记事本(NO.5):GraphQL 小实践的相关文章

使用微信小程序开发前端【快速入门】_javascript技巧

前言 2016年9月22日凌晨,微信官方通过"微信公开课"公众号发布了关于微信小程序(微信应用号)的内测通知.整个朋友圈瞬间便像炸开了锅似的,各种揣测.介绍性文章在一夜里诞生.而真正收到内测邀请的公众号据说只有200个. 虽然内测名额十分稀少,但依赖中国广大开发者的破解和分享精神,在网络上很快出现了开发工具的破解版本和API文档.然而可能是微信的妥协或者早已预料,9月24日微信官方发布了不需要破解就可以使用的微信小程序开发者工具和文档,对于费劲心思破解完的开发者来说应该瞬间整个人都不好

微信小程序开发教程第八章:微信小程序分组开发与左滑功能实现

接着上面微信小程序开发教程第八章:微信小程序分组开发与左滑功能实现.(第一二章:微信小程序开发教程,第三四章:微信小程序项目结构以及配置&微信小程序首页面开发,第五章:微信小程序名片夹详情页开发,第六章:「我」的页面开发,第七章:微信小程序编辑名片页面开发) 先来看看今天的整体思路: 进入分组管理页面-->点击新建分组新建 进入到未分组页面基本操作 进入到已建分组里面底部菜单栏操作-->从名片夹中添加进行操作. 理清完基本流程我们开始开发.首先整个布局可以先看下,我再一步步讲. 静态布

小程序-关于数组元素初始化有一些小问题

问题描述 关于数组元素初始化有一些小问题 RT 最近写的几个小程序中几乎所有的数组元素初始化都有问题,求大神看看是哪里有问题 //求一个4*4二维数组的对角线上的值的和 #include<stdio.h> int main (void) { int array[3][3],sum = 0; int i = 0,j = 0; printf("Please enter the numbers(16 numbers inside):n"); for (i = 0;i <=3

小程序.大商机.新未来”云指小程序研讨会成功举办

本文讲的是 : 小程序.大商机.新未来"云指小程序研讨会成功举办   ,8月5日,由云指主办的"小程序.大商机.新未来"小程序研讨会在佛山成功举办,会议汇聚了全国60多位互联网行业精英,共同探讨了微信小程序发展的前景,剖析了如何抢占小程序创造的千亿市场;同时对云指小程序产品进行了展示和体验. 本次小程序研讨会,云指邀请了广东医联网公司总裁.华南理工大学电子商务导师韦海先生就小程序入口.价值和未来的发展趋势进行了一个分享.小程序作为腾讯一个战略级别的产品,与微信强强联合,一端连

微软高管沈向洋:必应在华份额太小已非常危险

微软高管沈向洋新浪科技讯 3月25日凌晨消息,微软全球资深副总裁.搜索与广告首席科学家沈向洋昨日晚间接受新浪科技采访时表示,必应搜索在中国的市场份额已经跌到非常危险的地步,他希望必应能够以英文搜索为突破口在中国市场实现一定突破.必应在华份额危险2009年6月,微软搜索引擎必应正式在国内上线, 但是在过去3年多的时间里,必应 中文搜索表现欠佳,市场份额一直徘徊在1%之下.市场研究机构CNZZ的数据显示,2013年2月,必应在中国的市场份额为 0.52%.谈及过去几年的表现,沈向洋坦承,必应中文搜索

win10桌面版小娜好用吗?Win10版小娜Cortana使用评测

  语音助手Cortana(小娜)集成在了微软刚刚发布会的Windows 10系统里,微软操作系统部门高级副总裁Joe Belfiore刚刚演示了小娜在PC上的表现,问了她很多问题,都对答如流,整体上和手机上极为相似,甚至"Hey Cortana"都保留了下来.小娜可以帮你查询各种信息,比如新闻航班.股价.天气.体育比分等等.一起来看看了解一下吧! Win10版小娜Cortana,位于底部任务栏开始按钮右侧,支持使用语音唤醒.可以支持要求小娜为你打开相应的文件,也可以搜索本地文件, 现

前端跨域请求原理及实践

一. 跨域请求的含义 浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用. 一般的,只要网站的 协议名protocol. 主机host. 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用.这也是我们下面实践的理论基础.我们利用 NodeJs 创建了两个服务器,分别监听 3000. 3001 端口(下面简称 服务器3000 与 服务器3001 ),由于端口号不一样,这两个服务器以及服务器上页面通信构成了跨域请

java小问题-初学者问个关于java的小问题

问题描述 初学者问个关于java的小问题 char c = str.charAt(i); 这个语句是什么意思???求解答(谢谢!!) 解决方案 声明一个char型变量c,它的值是str的第i个位置的字符 解决方案二: 字符型c等于 str字符串的第i个元素 解决方案三: java小问题java中的小问题java 小问题 解决方案四: 同意楼上解答.这些都是非常非常基础的JAVA知识. 解决方案五: 声明一个char型变量c,它的值是str字符串的第i个位置的字符. 至于你说的,"不应该是Stri

小咖秀评论在哪里 如何查看小咖秀评论?

1.我们先找到视频了,然后点击进入,如下面我们点击"热门视频"小咖秀. 2.进入小咖秀个人主页后,我们就可以看评论了,当然也可以对单个视频进行评论了,打开进行然后就可以评了. 根据小编的经验总结一下其实我发现几乎所的可以评论的内容它的评论都会在当前的内容下方了,这个小咖秀也是一样的了,各位不信可以去尝试一下吧.