• GraphQL 入门与实践


    概述

    GraphQL 是一种新的 API 标准,由 Facebook 开发并开源。

    Facebook 在 2012 年就在他们移动端应用中使用上了 GraphQL,第一次公开发布是在 2015 年的 React Conf 上。其实不仅 Facebook,其他公司也有在探索相关类似的技术,Netflix 也曾推出他们的方案 Falcor,Coursera 在 Facebook 推出 GraphQL 后,取消了相关研发,直接使用 GraphQL。

    为什么会有这么多公司都在研发相关技术,让我们先看 GraphQL 是如何使用的,再看看它和现在的 RESTful api 的对比。

    GraphQL 入门示例

    我们将使用 apollo-server 来创建 GraphQL 示例,或者你也可以使用 express-graphql。示例来源 Apollo 官方,地址

    一个 GraphQL 服务是通过定义类型和类型上的字段来创建的,所以我们先定义类型和字段

    const { ApolloServer, gql } = require('apollo-server');
    
    // 定义了一个 Book 类型,后续我们可以在 Query 中使用
    const typeDefs = gql(`# 定义了一个 Book 类型,后续我们可以在 Query 中使用type Book {title: Stringauthor: String} # Query 类型是特殊,它列出所有客户端可查询的字段type Query {books: [Book]}
    `) 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    接着我们需要处理对应的字段解析函数,这边我们需要处理 books 是怎么获取数据的,最后返回一定是一个 Book 类型数组。

    const books = [{title: 'The Awakening',author: 'Kate Chopin',
    }, {title: 'City of Glass',author: 'Paul Auster',
    }];
    
    const resolvers = {Query: {books: () => books,}
    }; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    最后创建 ApolloServer 并启动,一个 GraphQL 服务就跑起来了。

    const server = new ApolloServer({ typeDefs, resolvers });
    server.listen().then(({ url }) => {console.log(`🚀 Server ready at ${url}`);
    }); 
    
    • 1
    • 2
    • 3

    本地启动服务后,你可以访问 http://localhost:4000/ 并进入 GraphQL playground 来体验(或者使用 apollo 官方 demo 体验)。根据定义的类型和类型上的字段,我们编写对应的查询语句即可获取到想要的数据。你也可以试着只返回 book 的 title 试试。

    从上面例子就可以体验到 GraphQL 的使用并不复杂,并且可以查询自己想要的数据。

    GraphQL 优点和缺点

    Facebook 设计出 GraphQL 是为了处理 RESTful API 的一些局限性, 所以我们先看下它的优点,再看 GraphQL 自己的局限性。

    优点

    1.Overfetching & Underfetching

    Overfetching 很好理解,平时我们用 RESTful API 请求数据的时候,接口往往会返回很多字段,有些字段是我们不需要的。而 GraphQL 可以指定要返回什么数据,就不会出现数据冗余。Fackbook 最先也是在移动端应用上使用 GraphQL 的,没有数据冗余对于移动端的访问来说可以更快的展示页面。

    Underfetching 指请求的返回的数据信息不够,这样导致我们需要发多个请求去获取数据。比如一个页面需要显示用户信息,包括基本信息和用户文章。这个需求对于 RESTful API 来说通常需要两个接口去获取,一个获取基本信息,一个获取用户的文章。但是对于 GraphQL 来说,一个请求就搞定,指定基本信息和文章的字段,接口自然返回相关数据。

    2.快速产品迭代

    RESTful API 在产品需求迭代中,很可能发生接口数量和接口 url 的变更,这些变更的影响范围可能会比较大;而 GraphQL 的接口只有一个,前端就只需关心自己想要的数据,可以更快速的进行产品迭代。

    3.灵活而强类型的 Schema

    GraphQL 是强类型的,查询基于字段及其关联的数据类型。如果 GraphQL 查询中存在类型不匹配,则服务端将返回明确且有用的错误消息。同时通过 Schema,前端和后端的工作可以各自开发,最后在联调。

    4.内省分析

    GraphQL 支持通过内省机制可以知道它支持哪些查询,这对于 GraphQL IDE 工具 非常友好,我们在上面的例子中就可以体验到。

    缺点

    1.HTTP 缓存

    由于 GraphQL 在 HTTP 上使用方式为单个端点中的 POST 请求,POST 请求的数据结构又是不固定的,所以无法在 HTTP 层上做数据缓存。社区是有提供一些客户端级别缓存方案,比如 Apollo Client,但是会增加开发成本。

    2.HTTP Status

    正常情况下 GraphQL 只会返回 Status Code 200,无论当前数据请求是成功或失败,这样传统方法的 HTTP 状态判断和逻辑就无法使用,虽然开发者可以自定义一套错误处理逻辑,但也增加了复杂度。

    返回 200 具体错误在 errors 中

    {"errors": [{"message": "Field "name" must not have a selection since type "String" has no subfields.","locations": [ {"line": 31,"column": 101 }]}
     ]
    } 
    
    • 1
    • 2
    • 3

    3.不可预测的执行

    GraphQL 的本质是你可以查询组合你想要的任何字段,但这种灵活性不是免费的。有一些问题值得了解,例如性能和 N+1 查询。

    GraphQL 使用 resolves 去获取字段的数据,每个字段都有执行 resolves,这就会对性能有一定的影响。同时如果查询中存在很深的嵌套,服务端需要一个机制去阻止这个性能昂贵的查询。

    4.处理文件上传

    GraphQL规范中没有关于文件上传的内容,并且不接受文件类型参数

    Learn GraphQL

    这个主要介绍 GraphQL 查询能力,对于服务端的 Schema 定义和实现,有兴趣的同学可以根据这个教程进行学习,How to GraphQL

    • 参数

    GraphQL 支持在请求时传递参数,每个字段都可以设置参数(服务端支持的情况下)。

    {human(id: "1000") {nameheight(unit: "cm")}
    } 
    
    • 1
    • 2
    • 别名

    当你想通过不同参数来查询相同字段就需要使用别名功能,比如下面的 hero 查询,在不使用别名的情况下你就无法构造出这个请求。

    {empireHero: hero(episode: EMPIRE) {name}jediHero: hero(episode: JEDI) {name}
    } 
    
    • 1
    • 2
    • 变量

    查询语句支持变量,使用 $ 符定义变量,并在发起请求时传入变量。

    # { "graphiql": true, "variables": { "episode": JEDI2 } }
    query HeroNameAndFriends($episode: Episode = "JEDI") {hero(episode: $episode) {namefriends {name}}
    } 
    
    • 1
    • 2
    • 3
    • 片段

    片段使你能够组织一组字段,然后在需要它们的地方引入,同时片段内也可以使用变量

    query HeroComparison($first: Int = 3) {leftComparison: hero(episode: EMPIRE) {...comparisonFields}rightComparison: hero(episode: JEDI) {...comparisonFields}
    }
    
    # 定义了一个片段,并在上面查询中使用
    fragment comparisonFields on Character {name# 使用了 HeroComparison 的变量friendsConnection(first: $first) {totalCountedges {node {name}}}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 内联片段

    你查询的字段返回的是接口或者联合类型,那么你可能需要使用内联片段来取出下层具体类型的数据

    ## hero 可能是 Droid 或者 Human 类型,各自类型返回各自数据
    query HeroForEpisode($ep: Episode!) {hero(episode: $ep) {name... on Droid {primaryFunction}... on Human {height}}
    }
    
    
    # 你也可以通过片段方式来实现
    query GetMyProfile {me {nameprofilePicture...HostProfileFields...GuestProfileFields}
    }
    fragment HostProfileFields on Host {profileDescription
    }
    fragment GuestProfileFields on Guest {funds
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 指令

    一个指令可以附着在字段或者片段包含的字段上,根据指令就会有不同的返回数据

    # @include(if: Boolean) 仅在参数为 true 时,包含此字段。
    query Hero($episode: Episode, $withFriends: Boolean!) {hero(episode: $episode) {namefriends @include(if: $withFriends) {name}}
    }
    
    
    # @skip(if: Boolean) 如果参数为 true,跳过此字段。
    query Hero($episode: Episode, $withFriends: Boolean!) {hero(episode: $episode) {namefriends @skip(if: $withFriends) {name}}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • Mutation

    变更并查询这个字段的新值,类比 RESTful API 中的 POST、PUT 等方式请求

    mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {createReview(episode: $ep, review: $review) {starscommentary}
    } 
    
    • 1
    • 2
    • 元字段

    GraphQL 允许你在查询的任何位置请求 __typename,它是一个元字段,可以获得那个位置的对象类型名称

    # 在不适用 __typename 的情况下,你就无法知道这个 name 的字段是那个类型的
    {search(text: "an") {__typename... on Human {name}... on Droid {name}... on Starship {name}}
    }
    
    
    {"data": {"search": [{"__typename": "Human","name": "Han Solo"},{"__typename": "Human","name": "Leia Organa"},{"__typename": "Starship","name": "TIE Advanced x1"}]}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    总结

    RESTful 和 GraphQL 都是数据传输解决方案,GraphQL 可以显著的节省网络传输资源,在带宽紧张的环境中(例如移动端),这将发挥巨大的作用。尽管 GraphQL 相比 REST 有很多显著的优点和升级,但在真实场景中,它并不一定是最适合你的实现。

    总结来说,如果你希望做的应用追求简单而敏捷,且没有什么特殊考量,那就没什么必要使用 GraphQL,RESTful 可靠、经济、不易出错;反而言之,如果应用的关键点在于组织复杂数据逻辑,请求存在较多 Overfetching、Underfetching 的情况,或者对于网络环境敏感,可以尝试 GraphQL。

  • 相关阅读:
    开发工具:推荐几款非常漂亮的VScode主题
    Win10重启后总是自动打开上次未关闭的程序怎么办
    使用比值校正法消除信号中正弦信号的干扰
    198. 打家劫舍
    Altium Designer2022相关操作
    幸运红包娱乐微信小程序源码下载-多玩法安装简单
    Linux项目自动化构建工具-make/Makefile
    【步骤详解】Docker一键部署微服务详细教程
    制作一个简单HTML个人网页网页(HTML+CSS)大话西游之大圣娶亲电影网页设计
    【汇编】“转移”综述、操作符offset、jmp指令
  • 原文地址:https://blog.csdn.net/qq_53225741/article/details/126159166