美文网首页
GraphQL从入门到实践

GraphQL从入门到实践

作者: zhiqiangx | 来源:发表于2019-05-23 16:31 被阅读0次

    GraphQL是一种新的Api请求规范,它可以让客户端在请求中指定希望得到的数据,而不像REST那样只能在服务端进行预定义。GraphQL 的操作类型可以是 query、mutation 和subscription,描述客户端希望进行什么样的操作:

    • query 查询:获取数据
    • mutation 变更:对数据进行变更,比如增加、删除、修改
    • substription 订阅:当数据发生更改,进行消息推送

    GraphQL入门

    GraphQL为了方便人们学习,提供了Graphpack用来搭建零配置服务器,Graphpack会读取并发布src目录下的.graphql和resolvers.js。

    mkdir graphql-demo
    cd graphql-demo
    npm install
    npm install --save-dev graphpack
    
    // 打开package.json 输入
    "scripts": {
        "dev": "graphpack",
        "build": "graphpack build"
    }
    
    // 打开src/schema.graphql,输入
    type Query {
      hello: String
    }
    
    // 打开src/resolvers.js,写入以下代码:
    const resolvers = {
      Query: {
        hello: () => "Hello World!"
      }
    };
    export default resolvers;
    

    然后在graphql-demo目录下执行npm run dev,打开http://localhost:4000就可以看到 GraphQL Playground。GraphQL Playground是GraphQL提供的前端页面,封装了http request。GraphQL Playground左边是输入,中间有个执行按钮,点击执行按钮会将我们输入的内容放入http request body发送给我们刚启动的服务器,进过GraphQL解析会执行相应的resolvers函数,GraphQL Playground获取到请求结果并在右边呈现。
    之前我们在服务器端定义了Query类型的hello和Query类型的resolver解析函数hello,那我们要怎么获取到返回值Hello World!呢?在GraphQL Playground左边输入并执行以下代码:

    // 输入
    query {
      hello
    }
    
    // 输出
    {
      "data": {
        "hello": "Hello world!"
      }
    }
    

    更多关于GraphQL的使用可以阅读GraphQL文档来学习。下面提供了query、mutation 和subscription的例子,供大家参考学习。

    // db.js
    export let users = [
      { id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
      { id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
    ];
    
    // schema.graphql
    type Query {
        hello: String
        users: [User!]!
        user(id: ID!): User!
    }
    
    type User {
        id: ID!
        name: String!
        email: String!
        age: Int
    }
    
    type Mutation {
        createUser(id: ID!, name: String!, email: String!, age: Int): User!
        updateUser(id: ID!, name: String, email: String, age: Int): User!
    }
    
    type Subscription {
        subsUser(id: ID!): User
    }
    
    // resolvers.js
    import { users } from "./db";
    const {PubSub, withFilter} = require('apollo-server')
    const pubsub = new PubSub()
    const USER_UPDATE_CHANNEL = 'USER_UPDATE'
    
    const resolvers = {
      Query: {
        hello: () => "Hello World!",
        user: (parent, { id }, context, info) => {
          return users.find(user => user.id == id);
        },
        users: (parent, args, context, info) => {
          return users;
        }
      },
      Mutation: {
        createUser: (parent, { id, name, email, age }, context, info) => {
          const newUser = { id, name, email, age };
          users.push(newUser);
          return newUser;
        },
        updateUser: (parent, { id, name, email, age }, context, info) => {
          let newUser = users.find(user => user.id === parseInt(id));
          newUser.name = name;
          newUser.email = email;
          newUser.age = age;
          pubsub.publish(USER_UPDATE_CHANNEL, {subsUser: newUser})
          return newUser;
        }
      },
      Subscription: {
        subsUser: {
          subscribe: withFilter(
            (parent, {id}) => pubsub.asyncIterator(USER_UPDATE_CHANNEL),
            (payload, variables) =>  payload.subsUser.id === parseInt(variables.id)
          ),
          resolve: (payload, variables) => {
            console.log('🚢 接收到数据: ', payload)
            return payload.subsUser
          }
        }
      }
    };
    
    export default resolvers;
    

    测试例子

    Query测试例子
    // hello
    query {
      hello
    }
    // users(GraphQL返回值)
    query {
      users {
        id
        name
      }
    }
    // user (GraphQL参数)
    query {
      user(id: 1) {
        id
        name
      }
    }
    
    Mutation测试例子
    // 可执行query users查看结果
    mutation {
      createUser(id: 3, name: "xeon", email: "xeon@qq.com", age: 22) {
        id
        name
        email
        age
      }
    }
    
    Subscription测试例子

    在GraphQL Playground左上角新建两个tab,一个输入下面代码并执行,用于监听和相应数据更新

    subscription {
      subsUser(id: 2) {
        id
        name
        email
      }
    }
    

    另一个tab执行更新操作

    mutation {
      updateUser(id: 2, name: "xeon1", email: "test@qq.com") {
        id
        name
        email
      }
    }
    

    GraphQL与REST

    GraphQL在后端上是MVC中的Controller,用于定义API接口的参数和返回值,这似乎是和REST是一种竞争关系,而GraphQL提出“一切皆是图”口号也好像是在挑战REST提出的“一切都是资源“。但是冷静下来想一想,图也是一种资源,与其说GraphQL是颠覆REST的一种新技术,我更认可GraphQL是对REST的封装的说法,因为GraphQL在实现上主要干了两件事:

    • 提供了一个Api,用于接收请求数据
    • 根据schema.graphql解析请求数据,调用resolvers函数

    技术层面上GraphQL是对REST的封装,在设计体验上GraphQL确实给我们带来了许多惊喜。就像文章开题所说的,GraphQL明显比REST更加灵活,在前后端交互上GraphQL也有许多明显的优势:

    • 减少HTTP请求,GraphQL可将业务模式自由组合,我们可以调用一次API就能获取到我们想要的数据
    • 提升HTTP请求速度,GraphQL可以指定需要的数据,避免传输无效的数据
    • 代码复用率高,减少后端代码量,特别是面向用户体验的API
    • GraphQL的Subscription封装了管道技术,更方便的实时交互
    • 减少前后端开发人员的沟通成本
      ···

    BFF下GraphQL的地位

    GraphQL在中台架构和BFF开发模式上的地位举足轻重。前端开发者选择GraphQL开发BFF层的无数种理由:

    • GraphQL的类型系统和类型语言与前端的TS不谋而合,前端开发者对GraphQL的学习成本并不高
    • GraphQL提供了多种后端语言库,这让GraphQL可以很好的对接各种模式的业务中台
    • GraphQL与REST兼容,可以从REST平稳地过度到GraphQL
    • 虽然GraphQL建议我们把业务建模为图,但是这并不意味着GraphQL与关系型数据库是相悖的。“图”是通过GraphQL的类型语言来构建的,与Model层无关,更与数据库无关,因此GraphQL可以很好的兼容关系型数据库

    相关文章

      网友评论

          本文标题:GraphQL从入门到实践

          本文链接:https://www.haomeiwen.com/subject/nrovzqtx.html