【CuteJavaScript】GraphQL真香入门教程 上

简介: 【CuteJavaScript】GraphQL真香入门教程 上


看完复联四,我整理了这份 GraphQL 入门教程,哈哈真香。。。

欢迎关注我的 个人主页 && 个人博客 && 个人知识库 && 微信公众号“前端自习课”

首先有请阿爸镇贴!哈哈哈,需要高清原图的小伙伴可以 点我下载 阿爸无敌

下面开始本文内容:


一、GraphQL介绍

GraphQL 是 Facebook 开发的一种 API 的查询语言,与 2015 年公开发布,是 REST API 的替代品。

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

官网: graphql.org/

中文网: graphql.cn/

1. 特点

  • 请求你所要的数据,不多不少;

如:hero 中有 name, age, sex 等,可以只取得需要的字段。

  • 获取多个资源,只用一个请求;

典型的 REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样也能保证在较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。

  • 描述所有可能类型的系统。便于维护,根据需求平滑演进,添加或隐藏字段;

GraphQL 使用类型来保证应用只请求可能的数据,还提供了清晰的辅助性错误信息。应用可以使用类型,而避免编写手动解析代码。

2. 简单案例

这里先看下简单案例,体验下 GraphQL 的神奇之处(后面详细介绍)。

我们这样定义查询语句:

query {
    hero
}

然后得到的就是我们所要查询的 hero 字段:

{
    "data": {
        "hero": "I'm iron man"
    }
}

这样用起来,是不是更舒服呢?


二、GraphQL与restful对比

1. restful介绍

全称:Representational State Transfer 表属性状态转移。

本质上就是定义 uri ,通过 API 接口来取得资源。通用系统架构,不受语言限制。

例子: 饿了吗接口。

如:接口 restapi/shopping/v3/restaurants?latitude=13 就是个典型的 restful 接口,定义资源 + 查询条件。

2. 与 GraphQL 比较

  • restful 一个接口只能返回一个资源,GraphQL一次可以获取多个资源。
  • restful 用不同 url 来区分资源,GraphQL 用类型区分资源。

三、使用express构建基本helloworld

1. 简单案例

首先创建一个文件夹 demo ,并初始化一个 package.json,安装 express / graphql / express-graphql 依赖包:

npm init -y
npm install express graphql express-graphql -S

新建一个 hello.js,引入文件:

const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHTTP = require('express-graphql')

创建一个 schema 来定义查询语句和类型,buildSchema() 方法需要传入的参数是字符串类型,如下面的 hero 查询字段,后面的 String 类型表示字段返回的数据类型:

const schema = buildSchema(`
    type Query {
        hero: String
    }
`)

创建一个 root 处理器,处理对应的查询,这里的 hello 处理器对应的是 schema 中的 hero 字段查询的处理,这里直接返回 I'm iron man 的结果:

const root = {
    hero: () => {
        return "I'm iron man"
    }
}

当然,处理器中也可以是其他复杂操作,后面会介绍。

然后实例化 express ,并且将路由转发给 graphqlHTTP 处理:

const app = express()
app.use('/graphql', graphqlHTTP({
    schema,
    rootValue: root,
    graphiql: true
}))
app.listen(3000)

graphqlHTTP 中的三个参数介绍:

  • schema:定义的查询语句和类型
  • rootValue:处理对应查询的处理器
  • graphiql:是否开启调试窗口,开发阶段开启,生产阶段关闭

接下来运行项目,在命令行中执行 node hello.js,这里可以在 graphiql 上做调试,打开地址 localhost:3000/graphiql 就可以愉快的查询了。

另外我们可以在 graphiql 界面右侧打开 Docs 查看我们定义的所有字段和描述信息。

最终代码:

const express = require('express')
const { buildSchema } = require('graphql')
const graphqlHTTP = require('express-graphql')
// 构建schema,这里定义查询的语句和类型
const schema = buildSchema(`
    type Query {
        hero: String
    }
`)
// 定义查询所对应的 resolver,也就是查询对应的处理器
const root = {
    hero: () => {
        return "I'm iron man"
    }
}
const app = express()
// 将路由转发给 graphqlHTTP 处理
app.use('/graphql', graphqlHTTP({
    schema: schema,
    rootValue: root,
    graphiql: true
}))
app.listen(3000)

2. 自定义类型查询

我们前面的查询中,已经将 hero 字段定义为 String 类型,但是常常开发中,我们又会碰到字段是多个类型,即字段也能指代对象类型(Object),比如一个 user 字段会有 nameage 等字段,而 name 返回字符串类型,age 返回数值类型。

这时候,我们可以对这个对象的字段进行次级选择(sub-selection)。GraphQL 查询能够遍历相关对象及其字段,使得客户端可以一次请求查询大量相关数据,而不像传统 REST 架构中那样需要多次往返查询。

我们可以新建一个查询类型来定义 user 字段返回的类型:

const schema = buildSchema(`
    type User {
        # 查询可以有备注!
        name: String
        age: Int
    }
    type Query {
        hero: String
        user: User
    }
`)

在处理器中我们也要加上:

const root = {
    hero: () => {
        return "I'm iron man"
    },
    user: () => {
        return {
            name: 'leo',
            age: 18
        }
    }
}

这边 Int/String 参数类型的问题,下一章介绍


四、参数类型和参数传递

1. 基本参数类型

String, Int, Float, BooleanID,这些基本参数类型可以在 schema 声明中直接使用。

  • Int:有符号 32 位整数。
  • Float:有符号双精度浮点值。
  • StringUTF‐8 字符序列。
  • Booleantrue 或者 false
  • IDID 标量类型表示一个唯一标识符,通常用以重新获取对象或者作为缓存中的键。ID 类型使用和 String 一样的方式序列化;然而将其定义为 ID 意味着并不需要人类可读型

另外,我们可以使用 [类型] 来表示一类数组,如:

  • [Int] 表示整型数组;
  • [String] 表示字符串型数组;

2. 参数传递

使用方式和 JS 参数传递一样,小括号内定义形参,但是参数需要定义类型

使用 ! 代表参数不能为空。

下面案例:参数 teamNameString 类型,必须传递,而 number 参数也是 Int 类型,但是是非必须传递,最后输出的结果也是 String 类型。

type Query {
    getHero(teamName: String!, number: Int): [String]
}

下面一个案例:

//...省略其他
const schema = buildSchema(`
    type Query {
        getHero(teamName: String!): [String]
    }
`)
const root = {
    getHero: ({teamName}) => {
        // 这里的操作 实际开发中常常用在请求数据库
        const hero = {
            '三国': ['张飞', '刘备', '关羽'],
            '复仇者联盟': ['钢铁侠', '美国队长', '绿巨人']
        }
        return hero[teamName]
    }
}
//...省略其他

这时候我们在 GraphiQL 上输入查询,就会得到 复仇者联盟 的英雄数据了。

// 查询
query {
  getHero(teamName:"复仇者联盟")
}
// 结果
{
    "data": {
        "getHero": [
            "钢铁侠",
            "美国队长",
            "绿巨人"
        ]
    }
}

3. 自定义返回类型

在实际开发中,我们返回的数据类型可能是一个对象,对象中可能既有 Int 类型的属性,也有 String 类型的值,等等,这里我们可以使用 自定义返回类型 来处理:

//...省略其他
const schema = buildSchema(`
    type Hero {
        name: String
        age: Int
        doSomething(thing: String): String
    }
    type Query {
        getSuperHero(heroName: String!): Hero
    }
`)
const root = {
    getSuperHero: ({heroName}) => {
        // 这里的操作 实际开发中常常用在请求数据库
        const name = heroName
        const age = 18
        const doSomething = ({thing}) => {
            return `I'm ${name}, I'm ${thing} now`
        }
        return { name, age, doSomething }
    }
}
//...省略其他

这里指定了 getSuperHero 字段的返回类型是 Hero 类型,随后在上面定义了 Hero

其中 Hero 类型中的 doSomething也是可以传递指定类型参数,并且指定返回类型。

下面看下输出情况:

// 查询
query {
  getSuperHero(heroName:"IronMan") {
        name
        age
        doSomething
  }
}
// 结果
{
    "data": {
        "getSuperHero": {
            "name": "IronMan",
            "age": 46,
            "doSomething": "I'm IronMan, I'm undefined now"
        }
    }
}

这里也可以给 doSomething 传递参数,就会获取到不同结果:

// 查询
query {
  getSuperHero(heroName:"IronMan") {
        name
        age
      doSomething(thing:"watching TV")
  }
}
// 结果
{
    "data": {
        "getSuperHero": {
            "name": "IronMan",
            "age": 46,
            "doSomething": "I'm IronMan, I'm watching TV now"
        }
    }
}


五、GraphQL客户端

这一节我们学习如何在客户端中访问 graphql 的接口。

1. 后端定义接口

我们先在后端将接口开发完成,这里跟前面差不多,但需要多一步,使用 express 向外暴露一个文件夹,供用户访问静态资源文件:

这里直接使用前一节的代码啦~

// index.js  开发 graphql 接口
//...省略其他
const schema = buildSchema(`
    type Hero {
        name: String
        age: Int
        doSomething(thing: String): String
    }
    type Query {
        getSuperHero(heroName: String!): Hero
    }
`)
const root = {
    getSuperHero: ({heroName}) => {
        // 这里的操作 实际开发中常常用在请求数据库
        const name = heroName
        const age = 46
        const doSomething = ({thing}) => {
            return `I'm ${name}, I'm ${thing} now`
        }
        return { name, age, doSomething }
    }
}
const app = express()
app.use('/graphql', graphqlHTTP({
    schema, rootValue: root, graphiql: true
}))
// 公开文件夹 使用户访问静态资源
app.use(express.static('public'))
app.listen(3000)

这样我们就给前端页面提供一个可以访问静态资源的功能。

这里还需要在根目录创建一个 public 文件夹,并在文件夹中添加 index.html 文件,此时的目录结构:

|-node_modules
|-public
|---index.html
|-index.js
|-package.json

2. 前端页面请求

然后给 index.html 添加按钮和事件绑定:

这里的变量 query 是个字符串类型,定义查询条件,在条件 GetSuperHero 中的参数,需要用 $ 符号来标识,并在实际查询 getSuperHero 中,作为参数的参数类型设置进来。

然后定义变量 variables ,指定属性的值,之后通过 fetch 发起请求:

<button onclick="getData()">获取数据</button>
<script>
function getData(){
    const query = `
        query GetSuperHero($heroName: String, $thing: String){
            getSuperHero(heroName: $heroName){
                name
                    age
                    doSomething(thing: $thing)
            }
        }
    `
    // 如果不需要其他参数 至少要传一个参数 否则会报错
    // const query = `
    //     query GetSuperHero($heroName: String){
    //         getSuperHero(heroName: $heroName){
    //              name
    //         }
    //     }
    // `    
    const variables = {heroName: '钢铁侠', thing: 'watching TV'}
    fetch('./graphql', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        },
        body: JSON.stringify({
            query, variables
        })
    })
    .then(res => res.json())
    .then(json => {
        console.log(json)
    })
}
</script>

当我们写完以后,点击 获取数据 就会在控制台打印下面的数据:

{
    "data":{
        "getSuperHero":{
            "name":"钢铁侠",
            "age":46,
            "doSomething": "I'm 钢铁侠, I'm watching TV now"
        }
    }
}

3. 注意点

  • 请求中的 query 参数需要对照好有 $ 符号的变量。

查询语句 query GetSuperHero($heroName: String) 里参数 $heroName 中的 heroName

查询语句 getSuperHero(heroName: $heroName) 里类型 $heroName 中的 heroName

变量 variables 中的 heroName 属性;

这三个名称需要一样

  • 请求中需要将数据序列化操作
body: JSON.stringify({ query, variables })



目录
相关文章
|
2月前
|
存储 关系型数据库 MySQL
|
2月前
|
前端开发 API UED
深入浅出GraphQL:理解与实践
本文将以清晰易懂的方式介绍GraphQL的概念及其实际应用。通过对比RESTful API和GraphQL的特点,阐述GraphQL在数据查询和交互方面的优势。同时,将探讨GraphQL在现代软件开发中的实际应用,并提供一些最佳实践指南。无论您是初学者还是有经验的开发者,都能从本文中获得有益的启发和指导。
|
8月前
|
开发框架 小程序 JavaScript
微信小程序wepy框架入门教程-搭建开发环境(一)
微信小程序wepy框架入门教程-搭建开发环境(一)
200 0
|
11月前
|
关系型数据库 MySQL Go
Day06:GORM快速入门01 入门指南| 青训营
Day06:GORM快速入门01 入门指南| 青训营
102 0
|
前端开发
前端知识学习案例-GraphQl速览
前端知识学习案例-GraphQl速览
43 0
前端知识学习案例-GraphQl速览
|
SQL 前端开发 JavaScript
关于graphql快速入门
关于graphql快速入门
158 0
|
缓存 搜索推荐 JavaScript
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
背景 最近,栈长发现某些国内的开源项目都使用到了 Knife4j 技术,看名字就觉得很锋利啊! 是不是这样的缩写呢: Knife4j = Knife for Java ? Java 匕首? 看起来很牛逼的样子,当然,这是我简单的猜测,从字面上并不能猜到它是干嘛用的! 那么它究竟是一个什么样的框架呢?
|
Web App开发 NoSQL 中间件
【CuteJavaScript】GraphQL真香入门教程 下
【CuteJavaScript】GraphQL真香入门教程 下
119 0
|
JSON 前端开发 NoSQL
GraphQL 从入门到实践
本文首先介绍了 GraphQL,再通过 MongoDB + graphql + graph-pack 的组合实战应用 GraphQL,详细阐述如何使用 GraphQL 来进行增删改查和数据订阅推送,并附有使用示例,边用边学印象深刻~ 如果希望将 GraphQL 应用到前后端分离的生产环境,请期待后续文章。 本文实例代码:Github 感兴趣的同学可以加文末的微信群,一起讨论吧~
GraphQL 从入门到实践
|
缓存 JavaScript 前端开发
再见 Swagger UI,国人开源了一款超好用的 API 文档生成框架,Star 4.7K+,真香……
最近,栈长发现某些国内的开源项目都使用到了 Knife4j 技术,看名字就觉得很锋利啊!
789 0
再见 Swagger UI,国人开源了一款超好用的 API 文档生成框架,Star 4.7K+,真香……