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)部分业务在用。

  • 相关阅读:
    Kafka 生产者 自定义分区策略
    同步互斥
    poj 1562 Oil Deposits(dfs)
    poj 2386 Lake Counting(dfs)
    poj 1915 KnightMoves(bfs)
    poj 1664 放苹果(dfs)
    poj 1543 Perfect Cubes (暴搜)
    poj 1166 The Clocks (暴搜)
    poj 3126 Prime Path(bfs)
    处理机调度
  • 原文地址:https://www.cnblogs.com/dh-dh/p/10300412.html
Copyright © 2011-2022 走看看