V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
gzf6
V2EX  ›  程序员

关于 GQL 的新动态,各位怎么看,对前端后端有什么影响

  •  1
     
  •   gzf6 · 2018-11-08 09:48:21 +08:00 · 5076 次点击
    这是一个创建于 2207 天前的主题,其中的信息可能已经有所发展或是发生改变。
    48 条回复    2019-04-03 12:55:27 +08:00
    cyril4free
        1
    cyril4free  
       2018-11-08 09:50:10 +08:00
    隔壁组的同事已经用上了,据说很爽。。免去了前后端对接口参数的困扰。。
    fzleee
        2
    fzleee  
       2018-11-08 09:59:23 +08:00
    所以,GQL 是个什么东西...
    gzf6
        3
    gzf6  
    OP
       2018-11-08 10:00:45 +08:00
    heww
        4
    heww  
       2018-11-08 10:06:27 +08:00 via iPhone
    用起来很爽,不用费什么脑子。但它的 js client 不好用。
    IsaacYoung
        5
    IsaacYoung  
       2018-11-08 10:39:20 +08:00
    去 redux 化
    reus
        6
    reus  
       2018-11-08 10:57:06 +08:00
    如果需要高性能,后端就要手写,就没法自动生成代码
    如果自动生成代码,那性能…… 就好像查询不需要开销似的
    关系数据库本质不是图数据库,后端需要实现这两种模型之间的转换,没有简单高效的办法
    反正我是不想做这种东西,除非有图数据库可以替代关系数据库
    trait
        7
    trait  
       2018-11-08 11:08:58 +08:00 via iPhone
    应该不错,GitHub 的 gql 版本 api 已经有了,正在过渡,Twitter 貌似也在用
    TommyLemon
        8
    TommyLemon  
       2018-11-08 11:15:48 +08:00   ❤️ 3
    说明很多公司都前后端分离后碰到了各种恼人的开发与沟通问题

    1.浪费性能、流量和带宽
    返回不需要的字段、对象等

    2.各种奇葩的缩写、混乱的命名
    各种缩写、拼音、驼峰和非驼峰混用等,
    只有看文档才知道是什么、才知道用哪个,而且文档还很可能有错误。
    例如
    评论数量可能是 commentCount, comment_count, cmt_count, pl_num...
    分页页码可能是 page, pageNum, page_number, page_num, pnum...

    3.数据类型不稳定或随意改变
    对象为空时应该返回 null 或 {} ,但实际会返回空数组 [],
    甚至是 "", "[]" 等 ,导致前端解析崩溃;
    后端擅自改变类型导致线上 App 崩溃...

    4.几百甚至上千个混乱的状态码
    各项目几乎完全不通用,不看相关的内部文档根本不知道对应的错误是什么,而且文档还很可能有错误。
    例如
    注册: 成功 600, 手机号不合法 601, 验证码错误 603, 手机号已注册 607, 缺少必要字段 609...
    评论: 成功 1070, 不允许评论 1071, 参数错误 1073, 动态被删除 1075...

    5.文档过时,与接口不同步
    后端把接口改了没有及时通知,前端 /客户端莫名其妙调了半天才发现

    6.应用界面和接口强耦合难分离
    比如某个版本的 QQ 空间动态的 JSON 结构必须对应某个版本的某个接口,
    有时候 JSON 结构甚至是由后端拍脑袋决定的,不好用也得用

    7.版本迭代导致大量重复功能的接口
    为了兼容旧版应用不好直接改原来的接口,一般只能新增

    8.前端 /客户端与后端扯皮
    前端 /客户端想要一次性返回或者更方便解析的结构,但后端想要少写代码

    9.数据库操作不安全
    delete 忘加 where 直接删光全部数据,只要发生一次

    10.开发流程繁琐周期长
    后端写接口>后端写文档>前端 /客户端查文档>(前端 /客户端关于文档向后端提问>后端解决问题并通知或等待再次被问)>前端 /客户端调用接口>(前端 /客户端关于实际使用向后端提问>后端解决问题并通知或等待再次被问)>前端 /客户端解析返回结果>(前端 /客户端关于返回内容或结构向后端提问>后端解决问题并通知或等待再次被问)

    ...

    Facebook 出的 GraphQL 部分解决了这些问题,让它们看到了希望
    TommyLemon
        9
    TommyLemon  
       2018-11-08 11:18:31 +08:00
    @heww @reus @TommyLemon
    APIJSON 比 GraphQL 强大易用很多,解决了以上所有问题,
    并且不用写 Schema,Type,resolver 等一堆东西,
    它会自动将前端传的 JSON 参数转为 SQL 语句执行并返回结果,
    期间自动校验权限、结构、内容,自动防 SQL 注入。
    还有自动化的各种 JOIN(INNER, LEFT, RIGHT 等)解决 N+1 问题。
    还支持多字段排序 order by,多字段分组 group by,聚合函数 having
    等几乎所有 MySQL 的常规功能。
    juejin.im/post/5ae80edd51882567277433cf

    创作不易,GitHub 右上角点 Star 支持下吧,谢谢 ^_^
    github.com/TommyLemon/APIJSON
    find456789
        10
    find456789  
       2018-11-08 11:21:12 +08:00
    我是小白

    感觉权限是个问题呀, 比如 没有登陆会员只能查看 3 个字段,vip 能查看 5 个字段, 管理员能查看所有字段, 这在 gql 上是不是很难实现呀?
    TommyLemon
        11
    TommyLemon  
       2018-11-08 11:24:58 +08:00   ❤️ 1
    @find456789 逻辑不难,但判断要写很多,这里有教程
    juejin.im/post/5b13cda1f265da6e4a6bcfee
    Hilong
        12
    Hilong  
       2018-11-08 11:30:39 +08:00 via Android
    @TommyLemon 昨天还看到公众号推送去搜索了 下,支持
    asd123456cxz
        13
    asd123456cxz  
       2018-11-08 11:45:15 +08:00
    @TommyLemon #11 看到老哥推广好多次了。。支持下
    TommyLemon
        14
    TommyLemon  
       2018-11-08 11:54:52 +08:00
    @Hilong @asd123456cxz 感谢。
    其实我有时也在一些技术群推广 GraphQL,在公司也为 GraphQL 开过分享会。
    不怕对比,就怕大家不知道、不认可 前端定制接口 的做法。
    一部分用户是因为先了解到 GraphQL,然后看不懂、不会用、用起来麻烦,
    后面看到 APIJSON 就转了过来。
    RubyJack
        15
    RubyJack  
       2018-11-08 11:55:17 +08:00
    性能一塌糊涂
    clino
        16
    clino  
       2018-11-08 12:04:16 +08:00
    @TommyLemon 能用在 python 后端上吗?
    TommyLemon
        17
    TommyLemon  
       2018-11-08 12:09:44 +08:00
    @RubyJack GraphQL 是用来自动整合其它接口或服务的,性能取决于 整合方式 以及 原来的接口或服务的性能。
    如果就是简单地把几个接口拼在一起返回数据,很容易引发 N+1 次查询 /调用,性能就差了,
    所以 Facebook 又搞了个 [DataLoader]( https://github.com/facebook/dataloader),
    从主查询(主表)里取出 副查询(副表)需要的所有 id,
    将原来 N 次 WHERE id=$id 副查询 变为一次 WHERE id IN( $idList ) 来优化性能,
    但使用 DataLoader 要写 userLoader 等一些实例
    ```js
    var DataLoader = require('dataloader')

    var userLoader = new DataLoader(keys => myBatchGetUsers(keys));
    ```

    并且实现初始化
    ```js
    function createLoaders(authToken) {
    return {
    users: new DataLoader(ids => genUsers(authToken, ids)),
    }
    }
    ```

    和调用方法
    ```
    // Request begins...
    var userLoader = new DataLoader(...)

    // And a value happens to be loaded (and cached).
    userLoader.load(4).then(...)

    // A mutation occurs, invalidating what might be in cache.
    sqlRun('UPDATE users WHERE id=4 SET username="zuck"').then(
    () => userLoader.clear(4)
    )

    // Later the value load is loaded again so the mutated data appears.
    userLoader.load(4).then(...)

    // Request completes.
    ```

    在原来写一堆 Schema,Type,Resolver 的基础上又增加了不少工作量。

    APIJSON 直接提供自动化的 join,不需要后端写任何代码,前端传一个 join 键值对就行:
    ```js
    {
    "[]": { //查询数组
    "join": "</User/id@", // Comment LEFT JOIN User ON User.id = Comment.userId
    "Comment": {},
    "User": {
    "id@": "/Comment/userId",
    "@column": "id,name" // SELECT id,name
    }
    }
    }
    ```
    可以用 APIJSONAuto-自动化接口管理平台 在线测试
    http://apijson.org
    feverzsj
        18
    feverzsj  
       2018-11-08 12:11:51 +08:00
    前端老是喜欢搞些低能的东西
    TommyLemon
        19
    TommyLemon  
       2018-11-08 12:12:20 +08:00
    @clino
    目前只有 Java,C#, PHP, Node.js 的后端实现 以及 Android,iOS,JavaScript 的 Demo。
    但 APIJSON 的协议是与语言无关的,可以用 Python 等其它语言实现,可以参考这个引导
    github.com/TommyLemon/APIJSON/issues/38
    buhi
        20
    buhi  
       2018-11-08 12:16:52 +08:00
    大兄弟, 你怎么保证你这个自己一个人在做的东西就能比 fb 一个大厂在 backup 的东西要好用呢
    TommyLemon
        21
    TommyLemon  
       2018-11-08 12:40:14 +08:00
    @buhi
    不是保证,是基于事实的对比。
    APIJSON 与 GraphQL 全方位对比解析(一)-基础功能
    juejin。im/post/5ae80edd51882567277433cf

    APIJSON 与 GraphQL 全方位对比解析(二)-权限控制
    juejin。im/post/5b17518c6fb9a01e75463096

    APIJSON 与 GraphQL 全方位对比解析(三)-表关联查询
    juejin。im/entry/5b4ff88f6fb9a04f914a8df5

    如果你单纯用影响力来对比,那 OKHTTP,Mybatis,RxJava,Vue.js 等
    早期由个人开发者做起来的项目都不能和 FLAG 等大公司同类项目比了,更不可能超过了。

    GraphQL 硬生生地把加强版 API Proxy 做成了一门编程语言(#import,$variable...),
    基础的问题(定制结构、分组排序、JOIN 等)没都解决好,还越做越复杂,概念满天飞。
    那套新的协议还真只有 Facebook 那种水平的大牛才能准确高效地解析出来,
    不过毕竟是新协议,怎么都不会比发展了快 20 年的 JSON 更成熟方便。

    APIJSON 基于 JSON 扩展而来,不但提供了几乎所有 SQL 常用功能+远程函数等其它功能,
    还可以很好地利用 JSON 的生态,可以用一大堆成熟的封装与解析库,一大堆视频博客等教程,
    一大堆方便好用的调试工具(Chrome 控制台、Postman 等对 JSON 内置支持)...

    而且现在 APIJSON 主项目(Java)也有 4 个开发者在维护,支持 MySQL, PostgreSQL, Oracle。

    还有其它 3 个作者分别实现了 APIJSON 的 C#, PHP, Node.js 版。

    加上 APIJSONAuto 自动化接口管理工具( GraphiQL,Graphcool 的 PlayGround,Apollo Client Devtools,GraphQLIDE 好用很多,你可以都对比下)已经有一个初具雏形的 APIJSON 生态了。
    TommyLemon
        22
    TommyLemon  
       2018-11-08 12:43:44 +08:00
    @TommyLemon 是 APIJSONAuto 比它们好用很多,它们只在支持代码自动补全有优势,
    而 APIJSONAuto 的 自动注释请求、自动生成代码、自动化回归测试(前后对比测试+机器学习测试)是它们都没有的。
    http://apijson。org/
    kran
        23
    kran  
       2018-11-08 12:50:27 +08:00
    @TommyLemon apijson 怎么和权限认证配合的?
    reus
        24
    reus  
       2018-11-08 13:11:46 +08:00
    @TommyLemon 我们有自己的方案,不用你这破东西
    likuku
        25
    likuku  
       2018-11-08 13:17:40 +08:00   ❤️ 2
    刚看到阮一峰的推文: [看到一句妙语。“ GraphQL 的本质是程序员想对 JSON 使用 SQL。”]

    https://twitter.com/ruanyf/status/1060350454238859264
    zjsxwc
        26
    zjsxwc  
       2018-11-08 13:29:11 +08:00 via Android
    25 楼说的对,本质就是把 json 转 sql
    Narcissu5
        27
    Narcissu5  
       2018-11-08 13:34:25 +08:00
    第一反应这不就是当年的 web service 么,无非 xml 换成了 json。不知道为什么 facebook 总喜欢重蹈覆辙,react 也是一样的感觉
    buhi
        28
    buhi  
       2018-11-08 14:25:45 +08:00
    @Narcissu5 react 重蹈了什么?
    TommyLemon
        29
    TommyLemon  
       2018-11-08 14:30:26 +08:00   ❤️ 1
    @kran 3 行代码即可实现 各种角色 对一张表的 增删改查 默认权限配置,支持通过注解来自定义。
    my.oschina.net/tommylemon/blog/889074
    TommyLemon
        30
    TommyLemon  
       2018-11-08 14:32:10 +08:00
    @reus 还请您把方案拿出来和“我这破东西”对比下,让大家也长长见识,顺便给与支持
    vipppppp
        31
    vipppppp  
       2018-11-08 14:34:51 +08:00
    之前还没了解过,刚刚看了一下 GQL,下了个 python 的 demo 看了一下,感觉确实就是 json 转 sql
    至于 apijson 只能说我无福享受啊,毕竟我是个 python 程序员 = =
    TommyLemon
        32
    TommyLemon  
       2018-11-08 14:47:04 +08:00   ❤️ 1
    @vipppppp 唉,之前有个开发者在 issue 里留言说在做 Python 版:
    “刚刚把单元测试写好。json 的表现层完全自定义,都是为了对应 sql ”
    到现在有 2 个月了也没更新
    github。com/TommyLemon/APIJSON/issues/38
    Narcissu5
        33
    Narcissu5  
       2018-11-08 15:07:17 +08:00
    @buhi 逻辑和展示高度耦合
    buhi
        34
    buhi  
       2018-11-08 15:27:15 +08:00
    @Narcissu5 我觉得 react 只是说很容易写出逻辑和展示高度耦合的代码而已, 并不是 react 自己有在鼓励这种写法.
    whypool
        35
    whypool  
       2018-11-08 16:09:08 +08:00
    fb 出的东西,真的不敢恭维,也就剩下 fb 这大厂在背书了

    如果这是其他公司出的,估计会被喷成翔,即使红透了的 react 也一样
    sologgfun
        36
    sologgfun  
       2018-11-08 16:47:23 +08:00
    有点意思 马克一下
    reus
        37
    reus  
       2018-11-08 16:54:57 +08:00
    @Narcissu5 逻辑也是显示逻辑,不耦合干嘛?
    neoblackcap
        38
    neoblackcap  
       2018-11-08 17:10:29 +08:00
    理论上这个东西要优化,那么就得把数据库的查询优化器提到应用层,要不然上面所说的 N+1 问题,性能就会马上爆炸
    eloah
        39
    eloah  
       2018-11-08 18:26:16 +08:00
    之前用了一下,很多蛋疼的地方
    比如复杂的权限控制,比如还要自己弄 data loader
    TommyLemon
        40
    TommyLemon  
       2018-11-08 18:29:59 +08:00
    @eloah 哈哈,可以看下 APIJSON,提供自动化的权限控制,自动化 join,#9 楼 #21 楼有详细对比
    buhi
        41
    buhi  
       2018-11-08 18:46:16 +08:00
    N+1 也不是很难解决, 二三十行代码就能变成 1+1
    TommyLemon
        42
    TommyLemon  
       2018-11-08 18:50:39 +08:00
    @buhi 问题是所有需要优化的 Type 都要各自写一个 DataLoader, @eloah 这个兄弟应该深有体会
    buhi
        43
    buhi  
       2018-11-08 18:55:12 +08:00
    没搞懂问题在哪儿? 你不是本来就得对每个要查询的 Type 写一个 resolve 吗?
    MeteorCat
        44
    MeteorCat  
       2018-11-08 18:57:48 +08:00 via Android
    性能和效率的博弈
    TommyLemon
        45
    TommyLemon  
       2018-11-08 19:00:16 +08:00
    要写大量逻辑重复又不好抽象的代码,就是问题啊
    TommyLemon
        46
    TommyLemon  
       2018-11-08 19:08:02 +08:00
    @TommyLemon
    从 graphql-js 摘取片段代码
    github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsSchema.js

    ```js
    const humanType = new GraphQLObjectType({
    name: 'Human',
    description: 'A humanoid creature in the Star Wars universe.',
    fields: () => ({
    id: {
    type: GraphQLNonNull(GraphQLString),
    description: 'The id of the human.',
    },
    name: {
    type: GraphQLString,
    description: 'The name of the human.',
    },
    friends: {
    type: GraphQLList(characterInterface),
    description:
    'The friends of the human, or an empty list if they have none.',
    resolve: human => getFriends(human),
    },
    appearsIn: {
    type: GraphQLList(episodeEnum),
    description: 'Which movies they appear in.',
    },
    homePlanet: {
    type: GraphQLString,
    description: 'The home planet of the human, or null if unknown.',
    },
    secretBackstory: {
    type: GraphQLString,
    description: 'Where are they from and how they came to be who they are.',
    resolve() {
    throw new Error('secretBackstory is secret.');
    },
    },
    }),
    interfaces: [characterInterface],
    });


    const queryType = new GraphQLObjectType({
    name: 'Query',
    fields: () => ({
    hero: {
    type: characterInterface,
    args: {
    episode: {
    description:
    'If omitted, returns the hero of the whole saga. ' +
    'If provided, returns the hero of that particular episode.',
    type: episodeEnum,
    },
    },
    resolve: (root, { episode }) => getHero(episode),
    },
    human: {
    type: humanType,
    args: {
    id: {
    description: 'id of the human',
    type: GraphQLNonNull(GraphQLString),
    },
    },
    resolve: (root, { id }) => getHuman(id),
    },
    droid: {
    type: droidType,
    args: {
    id: {
    description: 'id of the droid',
    type: GraphQLNonNull(GraphQLString),
    },
    },
    resolve: (root, { id }) => getDroid(id),
    },
    }),
    });
    ```

    从 dataloader 摘取片段代码
    github.com/facebook/dataloader/blob/master/examples/SQL.md

    ```js
    var DataLoader = require('dataloader');
    var sqlite3 = require('sqlite3');

    var db = new sqlite3.Database('./to/your/db.sql');

    // Dispatch a WHERE-IN query, ensuring response has rows in correct order.
    var userLoader = new DataLoader(ids => {
    var params = ids.map(id => '?' ).join();
    var query = `SELECT * FROM users WHERE id IN (${params})`;
    return queryLoader.load([query, ids]).then(
    rows => ids.map(
    id => rows.find(row => row.id === id) || new Error(`Row not found: ${id}`)
    )
    );
    });

    // Parallelize all queries, but do not cache.
    var queryLoader = new DataLoader(queries => new Promise(resolve => {
    var waitingOn = queries.length;
    var results = [];
    db.parallelize(() => {
    queries.forEach((query, index) => {
    db.all.apply(db, query.concat((error, result) => {
    results[index] = error || result;
    if (--waitingOn === 0) {
    resolve(results);
    }
    }));
    });
    });
    }), { cache: false });

    // Usage

    var promise1 = userLoader.load('1234');
    var promise2 = userLoader.load('5678');

    Promise.all([ promise1, promise2 ]).then(([ user1, user2]) => {
    console.log(user1, user2);
    });
    ```
    mingyun
        47
    mingyun  
       2019-01-06 16:02:21 +08:00
    @TommyLemon GraphQL 这么 6
    karllynn
        48
    karllynn  
       2019-04-03 12:55:27 +08:00
    这个东西感觉只有内部系统可能会用一下,前端的权限太高了,而且性能很难保证,个人不看好。而且我记得以前就有项目可以前端直接写 sql 了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1236 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 18:16 · PVG 02:16 · LAX 10:16 · JFK 13:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.