GraphQL入门&server搭建(一)

GraphQL是什么?

GraphQL来自Facebook,在2015年开源。它是一种API查询语言。回想一下SQL,是不是有点惊人的相似?它们都是query language。SQL是Structured Query Language,GraphQL很显然,是Graph Query Language。这时候,你们可能会问,为什么叫graph?

MySQL是SQL的实现,Apollo, Relay也是GraphQL规范的实现

SQL的数据源是数据库,GraphQL的数据源可以是restful API,各种服务/微服务,或者数据库

为什么叫graph?

在GraphQL的世界里,万物皆为图,可以把你的业务模型建模为图。由于图的结构更接近于自然世界,相比关系型数据库,在设计图数据库时,会省去一个图结构向关系型结构的转化工作。

GraphQL有什么好处?

  • 前端灵活性。可以按需取字段,也可以对接口聚合,一次拿到所有的数据
  • 后端复杂度降低。不再需要维护接口的版本号了,减少冗余的业务代码(很可能会新增与现有API重复度较高的API,造成后端业务代码的冗余)
  • 强类型。完备的类型校验机制,提供了更健壮的接口
  • 可读性强,代码即文档

废话不多说,先来了解下GraphQL如何定义接口的,看下他的接口长什么样。

数据准备

首先准备一个简单的用户列表数据。注意:数据可来源于数据库、上游服务端/微服务端等,GraphQL本身不是数据库。

[
  { id: 1, key: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
  { id: 2, key: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
]
复制代码

Schema

Schema在GraphQL里相当于传统意义上的接口文档,可在schema中定义查询的name,入参和出参(返回的字段)。

每一个Schema都有一个Root QueryRoot Mutation

  • Query:定义查询动作
  • Mutation: 定义增删改的动作

基于刚刚准备的用户列表,我们来看下怎么编写和定义针对用户的接口。如下所示,会发现schema定义方式和typescript很像。

// schema.graphql
type Query {
  // 用户列表
  users: [User]!
  // 单个用户详情
  user (id: ID!): User!
}

type Mutation {
  // 添加用户
  addUser (id: ID!, name: String!, email: String!, age: Int): User
  // 更新用户
  updateUser (id: ID!, name: String, email: String, age: Int): User
  // 删除用户
  deleteUser(id: ID!): User!
}

type User {
  id: ID!
  key: Int!
  name: String!
  email: String!
  age: Int!
}
复制代码

type

从上面可以看出,类型type是schema的重要组成。强类型是graphql的重要特性之一。类型分为两类:

  • 标量类型(基础类型):Int(整型), Float(浮点型), String(字符串), Boolean(布尔型)和ID(唯一标识符类型)

枚举类型enum,是特殊的标量类型,可枚举的的可选值的集合

  • 对象类型:跟业务相关的类型,比如User类型。
type User {
  id: ID!
  key: Int!
  name: String!
  email: String!
  age: Int!
}
复制代码

注意:基础类型是叶子节点。定义对象类型时候,直到全部为基础类型

传递参数

// 参数id
type Query {
  user (id: ID!): User!
}
// 参数id, name, email, age
type Mutation {
  addUser (id: ID!, name: String!, email: String!, age: Int): User
}
复制代码

感叹号的含义

  • 表示从服务器获取这个字段,返回的是一个非空值
// 返回一个非空的User类型数组
type Query {
  users: [User]!
}
复制代码
  • 定义一个字段的参数,传递的参数不能是空值
// 传递的id为非空ID
type Query {
  user (id: ID!): User!
}
复制代码

复杂度分析

一个graphql的请求可能会给服务器带来巨大的压力,数据库操作太多的话,可能会导致DDoS攻击。复杂度分析通过@complexity实现

type Query {
  me: User @complexity(value: 10)
}
复制代码

废弃字段

可以标记某一个字段为废弃,并给出废弃原因。这样,在版本迭代时,就可以友好的提示到旧版本的使用者,促使其升级到最新的接口。废弃字段通过加@deprecated实现

type User {
  avatar: String @deprecated(reason: "Moved to UserAvatar.")
}
复制代码

已经了解了接口的定义,那各个接口如何去取数据和处理数据?graphql通过resolver来实现这点。

resolver

接下来,我们需要为Root Query(根查询)和Root Mutation(根变更)里面的每一个字段提供一个resolver的函数。

import {users} from '../db';
const resolvers = {
  Query: {
    users: (parent, args) => {
      return users
    },
    user: (parent, {id}) => {
      return users.find(item => item.id == id)
    }
  },
  Mutation: {
    updateUser: (parent, {id, name, age}) => {
      let user = users.find(item => item.id == id)
      user.name = name
      user.age = age
      return user
    }
  }
}

export default resolvers
复制代码

接下来,我们通过graphpack来搭建一个简单的graphql server

Graphpack实战

Graphpack可以创建一个简易的零配置的graphql server

Graphpack 是集成了 Webpack + Express + Prisma + Babel + Apollo-server + Websocket 的支持热更新的零配置 GraphQL 服务环境

首先,新建一个空项目,yarn init之后,安装graphpack

yarn add graphpack
复制代码

配置graphpack启动和打包脚本

// package.json
"scripts": {
    "dev": "graphpack",
    "build": "graphpack build"
}
复制代码

新建目录src,目录结构如下

企业微信截图_11e24ae4-59ee-4d9e-9803-918d2e54dc4e.png

  1. db/index中存放用户的mock数据
export let users = [
  { id: 1, key: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
  { id: 2, key: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
];
复制代码
  1. schema.graphql中定义schema
type Query {
  users: [User]!
  user (id: ID!): User!
}

type Mutation {
  addUser (id: ID!, name: String!, email: String!, age: Int): User
  updateUser (id: ID!, name: String, email: String, age: Int): User
  deleteUser(id: ID!): User!
}

type User {
  id: ID!
  key: Int!
  name: String!
  email: String!
  age: Int!
}
复制代码
  1. resolvers/index中定义各解析函数
import {users} from '../db';

const resolvers = {
  Query: {
    users: (parent, args, context, info) => {
      return users
    },
    user: (parent, {id}, context, info) => {
      return users.find(item => item.id == id)
    }
  },
  Mutation: {
    addUser: (parent, { id, name, email, age }, context, info) => {
      const user = { id, name, email, age }
      users.push(user)
      return user
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      const user = users.find(item => item.id == id)
      user.name = name
      user.email = email
      user.age = age
      return user
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id);
      if (userIndex === -1) throw new Error("User not found.");
      const deletedUsers = users.splice(userIndex, 1);
      return deletedUsers[0];
    }
  }
}

export default resolvers;
复制代码

yarn dev启动server,打开http://localhost:4000。在这个界面中,我们可以测试各接口,有点类似于postman。
先测试一下用户列表users和单个用户详情user的查询

query {
  users {
    key
    name
    age
    email
  }
  user (id: 1) {
    name
    age
  }
}
复制代码

企业微信截图_96b768cd-6b56-41a1-8595-d2a800489c49.png

再来测试一下更新用户mutation操作。更新id=1的用户信息

mutation {
  updateUser (id: 1, name: "lily", age: 20, email: "12@qq.com") {
    id
    name
    age
    email
  }
}
复制代码

QQ20210627-163809.png

更新完后,切换到users列表再刷新,发现数据已经变成最新的用户信息

QQ20210627-163945.png

其他的mutation变更操作类似,感兴趣的同学可以一一试试。

最后

graphql server端已经搭建好,在react客户端中怎么去请求?下一章节待续…

graphql中文文档

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享