zoukankan      html  css  js  c++  java
  • GraphQL

    GraphQL

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

    优点

    1. GraphQL可以让我们通过请求控制返回的字段,以此来减少restful api的设计理念带来的请求多次的问题。
      比如我们要获取指定id的文章相关信息,包括标题、作者、发布时间以及前两条评论;同时加载当前用户信息。
    // 两趟查询,难以拓展
    GET /user/111
    GET /article/1001?comment=2
    
    // 一趟查询,易于扩展
    {
      article (id: 1001){
        title,
        author,
        time,
        comments (first: 2)
          nickname,
          time,
          content
        }
      },
      user (id: 111){
        nickname,
        photo,
        sign
      }
    }
    
    1. 可拓展性
      前端自由选择返回
    2. 不需要额外代码处理冗余字段
    3. 提供 schema
      schema 可以在运行时被获取到, 类似thrift的idl文件,比较清楚,简单的方法甚至不需要文档。
    4. 调试比较方便

    缺点

    1. 缓存麻烦
      https://graphql.cn/learn/caching/
      Relay 和 apollo-client 是两个graphql的前端模块,可以帮你在前端缓存数据。
    2. GraphQL 在前端如何与视图层、状态管理方案结合,目前也只有 React/Relay 这个一个官方方案。
      换句话说,如果你不是已经在用 Node + React 这个技术栈,引入 GraphQL 成本略高,风险也不小,这就很大程度上限制了受众。
      而且 FB 官方就只有一个 Node.js 的 reference implementation,其他语言都是社区做的。
    3. 每一个 field 都对数据库直接跑一个 query,会产生大量冗余 query,虽然网络层面的请求数被优化了,但数据库查询可能会成为性能瓶颈。
      FB 本身没有这个问题,因为他们内部数据库这一层也是抽象掉的,写 GraphQL 接口的人不需要顾虑 query 优化的问题。
      如何解决?
      DataLoader, DataLoader 的主要功能是 batching & caching,帮你合并请求。
    4. 需要服务端的全力配合
    5. GraphQL不存在鉴权方案,需要自行解决

    get start

    GraphQL为express和koa提供了插件,可以方便的搭建GraphQL服务器。
    看下面的代码:

    var express = require('express');
    var graphqlHTTP = require('express-graphql');
    var { buildSchema } = require('graphql');
    
    // 使用 GraphQL schema language 构建 schema
    var schema = buildSchema(`
      input MessageInput {
        content: String
        author: String
      }
    
      type Message {
        id: ID!
        content: String
        author: String
      }
    
      type Query {
        getMessage(id: ID!): Message
      }
    
      type Mutation {
        createMessage(input: MessageInput): Message
        updateMessage(id: ID!, input: MessageInput): Message
      }
    `);
    
    // 如果 Message 拥有复杂字段,我们把它们放在这个对象里面。
    class Message {
      constructor(id, {content, author}) {
        this.id = id;
        this.content = content;
        this.author = author;
      }
    }
    
    // 映射 username 到 content
    var fakeDatabase = {};
    
    var root = {
      getMessage: function ({id}) {
        if (!fakeDatabase[id]) {
          throw new Error('no message exists with id ' + id);
        }
        return new Message(id, fakeDatabase[id]);
      },
      createMessage: function ({input}) {
        // Create a random id for our "database".
        var id = require('crypto').randomBytes(10).toString('hex');
    
        fakeDatabase[id] = input;
        return new Message(id, input);
      },
      updateMessage: function ({id, input}) {
        if (!fakeDatabase[id]) {
          throw new Error('no message exists with id ' + id);
        }
        // This replaces all old data, but some apps might want partial update.
        fakeDatabase[id] = input;
        return new Message(id, input);
      },
    };
    
    var app = express();
    app.use('/graphql', graphqlHTTP({
      schema: schema,
      rootValue: root,
      graphiql: true,
    }));
    app.listen(4000, () => {
      console.log('Running a GraphQL API server at localhost:4000/graphql');
    });
    

    我们通过GraphQL提供的语法,建立一个schema,类似typescript的interface。
    有多种基本类型:ID, String, Int, []。
    Query是查询操作,Mutation是增删改操作。
    所以这里,我们提供了一个getMessage的方法,返回Message类型的信息。
    Mutation类型这里我们提供了createMessage,和updateMessage两个方法。

    定义完schema,还需要定义方法的处理:
    定义root对象进行函数处理。

    执行node index.js。然后访问localhost:4000/graphql就可以看到相应的调试页面。(前提是graphiql:true)。

    这个调试页面还是很方便的。
    打开调试页面,先create一个message:

    然后查询这个message:

    type 为query的时候,可以不写query。

    前端请求

    var dice = 1;
    var sides = 6;
    
    var query = `query RollDice($dice: Int!, $sides: Int) {
      rollDice(numDice: $dice, numSides: $sides)
    }`;
    
    fetch('/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
      body: JSON.stringify({
        query,
        variables: { dice, sides },
      })
    })
      .then(r => r.json())
      .then(data => console.log('data returned:', data));
    

    这是官网给的试例。fetch请求,把query传过去就可以啦,如果请求的是个函数,需要传参variables。如上dice和sides两个参数

    连接数据库

    这里给个连接mysql的schema和rootValue

    
    var { buildSchema } = require('graphql');
    
    const mysqlCon = require('./mysql');
    
    // 使用 GraphQL schema language 构造一个 schema
    let count = 0;
    var schema = buildSchema(`
        type UserInfo {
            id: ID!
            name: String
            uid: Int
            age: Int
            sex: String
            createdTime: String
            updatedTime: String
            description: String
        }
        type Query {
            getUsers: [UserInfo]
            getUserById(id: ID!): UserInfo
        }
        type Mutation { 
            invokeCount: Int
        }
    `);
    
    // root 为每个端点入口 API 提供一个解析器
    var root = {
        async getUsers() {
            count += 1;
            let users = await mysqlCon.pifySelect('select * from Tab_User_Info');
            console.log(users);
            return users;
        },
        async getUserById({id}) {
            let users = await mysqlCon.pifySelect(`select * from Tab_User_Info where id=${id}`)
            return users[0];
        },
        invokeCount() {
            return count;
        }
    };
    
    module.exports = {
        root, 
        schema,
    }
    

    mysql.js

    const mysql = require('mysql');
    
    const connection = mysql.createConnection({
        host: '127.0.0.1',
        port: 3306,
        user: 'root',
        password: '123456',
        database: 'Test_User'
    });
    
    connection.connect(function(err) {
        if (err) {
            console.error('error: ' + err.stack);
            return;
        }
    });
    
    Object.defineProperty(connection, 'pifySelect', {
        value: function(sql) {
            return new Promise((resolve, _)=>{
                connection.query(sql, function (error, results) {
                    if (error) console.log('mysql select err:', error);
                    resolve(results);
                }); 
            })
        }
    })
    
    module.exports = connection
    

    用docker搞个mysql环境:

    docker pull mysql:5.6
    docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
    

    docker exec -it mysql bash进入bash然后初始化一下mysql,插几个数据用来操作。
    用koa-graphql或者express-graphql启一个服务,就可以直接访问啦。

    使用方

    facebook, twitter,github,我们公司(toutiao)部分业务在用。

  • 相关阅读:
    Python 模拟SQL对文件进行增删改查
    Python用户登陆
    计算程序的内存和占比
    列出top中的pid
    编写类du命令Python脚本
    生成器版本的文件MD5校验
    利用os、hash模块生成目录下所有文件的md5
    文件Copy和文件夹Copy
    Access数据库连接方式
    js常用方法收集
  • 原文地址:https://www.cnblogs.com/dh-dh/p/10300412.html
Copyright © 2011-2022 走看看