继全面采用Node.js以后,PayPal分享大幅度踩坑GraphQL心得 - Mark Stuart


这篇文章涵盖了我们在PayPal扩展GraphQL时学到的所有知识,并将作为在您的公司中部署GraphQL的指南。
一年前,我们撰写了“ GraphQL:PayPal Checkout的成功案例 ”,其中涵盖了我们从REST到批处理REST到GraphQL的历程。从那以后很多事情改变了!这篇文章是我们在PayPal 构建GraphQL API时获得的一系列最佳实践和观察的一部分。
一年前,使用GraphQL的产品很少。虽然我们在PayPal Checkout上取得了成功,但没有基础架构,工具,培训或支持。尽管存在这些差距,但GraphQL仍然像火箭一样起飞。在撰写本文时,我们有50个使用GraphQL的不同产品!
采用速度很快,像其他技术变革一样,企业级扩展与水平扩展或为服务器或云计算支付大量资金无关。扩展人员,工具和流程是最具挑战性的。

向内看
在部署GraphQL之前,重要的是要深入了解自己的公司,并反思自己是谁,干什么的以及优点和缺点是什么。
在PayPal,我们的产品使用JavaScript构建。后端使用Node.js对前端进行响应。堆栈中还有数百个Java REST服务和一些类似于C ++ SOAP的服务。
PayPal是最早使用Node的公司之一,并通过NodeDay之类的活动以及与Node FirmNodeSource的合作帮助启动了“企业中的Node”品牌。
PayPal在2013–2015年期间迁移到了Node。它改变了整个公司,并改善了我们制造和运输产品的方式。一个C ++单体应用程序简单的内容更改将需要数周时间才能部署。现在,开发人员可以在几分钟内进行试验,迭代和推出新产品功能。
那不是一夜之间发生的,不是偶然的。在Node加入PayPal之前,Bill Scott提出了使用LeanUX构建产品的愿景,其中迭代和学习是关键。在LeanUX中,UI位是实验性的并且是一次性的。如果实验效果不佳,请不断进行迭代!Node.js是成功的关键。
2014年,我们推出了Kraken,这是Express之上的一组库,旨在通过可配置的中间件,安全性默认设置,dust.js渲染器和内容本地化为您的应用“提供一些帮助”。现在,团队正在构建客户端React应用程序,而应用程序是部署到CDN的静态文件包,而不是在服务器群中运行的代码。
对于API,我们使用BFF(backend-for-frontend)模式。尽管它们非常专业,并且允许开发人员进行迭代,但是它们紧密耦合并且不能很好地重用。结果是我们有很多团队反复迭代和构建相同的事物!构建BFF API也不是一件容易的事。通常,它们包含许多编排逻辑,在这些逻辑中,您需要从5、10、15个不同的服务中获取数据,对这些响应进行规范化,丢弃95%的响应,然后将数据映射,过滤和分类为所需的数据。编写此代码并不能很好地利用我们的开发人员的时间。这种紧密的耦合和缺乏重复使用对我们来说是一个问题。

GraphQL可以提供帮助吗?是!

开发人员经验>性能
在跳入生产就绪项目清单之前,我们应该重新设定期望值。当您初次接触GraphQL时,通常会想到主要的好处是通过网络传输较少的数据,从而可以实现更快,更高性能的应用程序。
大型公司将GraphQL置于现有REST服务之上。结果是您的GraphQL查询将仅与最慢的REST服务一样快。GraphQL允许您在一次往返中获取所需的一切。如果客户端和服务器之间存在空闲,则可以减少往返行程并减少延迟。但是,这并不是您可以为每个人做出的承诺。
一段时间后,您将意识到开发人员 经验和灵活性的好处远比性能引人注目。
GraphQL对人类友好。使用GraphQL,开发人员可以根据字段而不是端点,域或复杂联接来考虑。开发人员可以遍历该图以选择用户的名字,60x60的个人资料照片,主要送货地址和信用卡,而不必调用6-10种不同的服务。新员工都喜欢它。如果您知道JSON是什么样子,请删除双引号和逗号,然后就可以查询GraphQL API。您的UI开发人员喜欢它,因为它以产品为中心,具有声明性,并具有丰富的工具集,可减少集成摩擦并提高信心。
当您将GraphQL推荐给公司的领导者和团队时,请关注开发人员的经验,生产力和灵活性。否则,您可能会让他们失望。

GraphQL位于堆栈边缘
在PayPal,我们的核心服务由独立的后端团队开发,我们的产品团队构建BFF API,以从这些基础核心服务中整理数据。最初,我们认为“ GraphQL应该无处不在”!
经过大量的实验和反思,我们发现GraphQL在堆栈的边缘发光最多
GraphQL以产品为中心,应该受到您的产品团队的影响(或开发)。GraphQL模式应在设计时首先由产品团队提供意见。后端开发人员不应孤立地设计它们。GraphQL做得很好。因此,GraphQL最适合您的堆栈边缘,并且可以与REST协同工作。


在公司启用GraphQL
好的,是时候实现这个了!本部分是在贵公司启用GraphQL的清单。
首先,您需要为产品团队奠定基础。GraphQL新颖且令人兴奋,有许多开放式问题和见解。几乎没有什么是一成不变的。您有很多选择!

  • 谁是您的API开发人员和消费者?
  • 谁来贡献?他们知道什么语言?
  • 他们今天如何构建API?它们是专门用途还是通用用途?
  • 您要使用现有的框架和工具吗?哪个?
  • 您需要添加公司特定的内容吗?身份验证,授权,重试,断路器,自定义HTTP状态代码,错误处理?
  • 您将如何执行标准?
  • 您将如何处理错误?

建立基础
在PayPal,GraphQL已与为BFF API和UI做出贡献的产品团队脱颖而出。我们为GraphQL API使用Node.js。与许多其他公司一样,我们使用Apollo开源库和工具Apollo拥有专门的团队来构建和维护这些工具,因此它们在一流的文档编制方面是一流的。我们使用apollo服务器,并在特定于PayPal的生产就绪状态下投入使用,例如日志记录和检测,身份验证,错误处理和速率限制。
如果您有任何独特的要求,请创建可与开源库(例如Apollo)一起使用的模块和插件。不要创建深层抽象或向开发人员隐藏复杂性。保持它可用于Google!
请记住,GraphQL仍然是一个API。您将要确保具有足够的日志记录,重试,断路器模式,速率限制查询复杂性检查。

扩展知识
确保架构师和API设计人员支持GraphQL,以帮助您扩展设计审查并执行标准。他们很可能已经设计了REST API多年。GraphQL是不同的。起初,您可能会遇到阻力。推动它。您需要花时间与他们一起概述差异,并挑战它们以不同的方式进行API设计:没有版本控制,拥抱图表而不是使用ID创建关系,没有HATEOAS链接。通过邀请架构师,您将不会是唯一的主题专家,它将为GraphQL带来合法性。

制定标准
您需要设定一些设计标准。创建一个文档并在任何地方引用它。使用诸如graphql-schema-linter之类的工具来强制命名约定。
一些示例包括:

  • 所有字段都必须有描述或注释
  • 类型取名:LikeThis,字段取名:LikeThis。枚举值:LIKE_THIS
  • 尽可能使用枚举
  • 弃用的字段必须有原因
  • 没有集合或列表后缀。(例如:使用cards,而不是cardList)
  • 偏好具有引起状态变化的输入类型

然后,您将需要围绕分页进行选择。在架构中显示列表的首选方式是什么?基于游标的分页
您将如何发现错误?在撰写本文时,我们错误处理还没有弄清楚,有很多选项可供选择:
  • 使用默认错误数组
  • 使用自定义属性扩展错误
  • 模式中的错误字段
  • Union types

在PayPal,我们选择使用自定义属性扩展错误。我们喜欢它仍然符合规范,并允许我们在需要时向错误添加错误分类和其他元数据。我们认为其他选项无法实现,并且允许错误不被注意到。我们将在以后的中型帖子中提供更多详细信息。

认证/授权
首先,我们保护了整个架构,然后意识到我们拥有许多具有不同特权的不同类型的用户。然后,我们创建了一个高阶auth函数,您可以使用它包装解析器。最后,我们意识到创建自定义auth指令是保护架构的最佳方法。

通过出色的工具获得超能力
之前,我们撰写了《GraphQL:检测您的API和释放超能力》,它解释了GraphQL与REST相比有何独特优势,您可以更好地了解API的使用方式,并在与API集成并保护客户端的过程中为客户提供额外的信心应对重大变化。
对于初学者,您可以将延迟,错误和使用情况数据通过管道传输到公司的监控仪表工具。
我们建议使用的其他工具包括:graphql-playground用于测试开发模式下的查询,graphql-schema-linter用于强制执行模式命名约定,eslint-plugin-graphql用于对模式查询客户端查询,graphql-doctor用于PR状态检查。
Apollo Graph Manager提供的仪器工具可显示现场级别的深刻见解,对突破性变化的信心,列入白名单的查询,并通过为每个字段提供衬布和在线SLA计时来简化客户集成。
Apollo Graph Manager不是免费的。我们可以自己建造吗?也许可以,但不会那么完美。我们没有GraphQL基础架构团队。如果我们这样做了,我们就不想等待12-18个月来构建可比较的东西并必须对其进行维护。我们现在想兑现GraphQL的承诺!从购买与构建的决策来看,Apollo Platform是一个明确的购买。我们建议您也考虑一下。

在企业中扩展GraphQL面临的挑战
到目前为止,GraphQL很棒,但是直到我们解决了它的组装方式和成功度的一些问题之前,它才是很棒的。
在2012年之前,PayPal是C ++ monorepo。从那时起,我们在许多领域和产品团队中产生了数千个GitHub存储库,构成了服务和应用程序层。对于拥有monorepos的Facebook和GitHub这样的公司来说,共享并不是什么大问题。
GraphQL在许多存储库中都很棘手。如果没有诸如自定义远程类型之类的自定义技巧,就无法引用或链接本地文件系统中不存在的远程类型。开发人员发现或重用其他服务中定义的类型并不容易。

组装GraphQL
用户希望看到一个可以浏览的单个内聚图,而无需考虑许多服务,而必须使用许多服务来获取所需的数据。实际上,组装单个图形是困难的。
一种解决方案是模式拼接,其中网关使用底层GraphQL API的模式并向开发人员显示单个模式,并将传入的查询委派给底层API。Marc-Andre Giroux写了一篇出色的文章,介绍了模式缝合的挑战。通过模式缝合,网关具有粘合代码,负责维护类型之间的关系并确保正确执行子查询。该胶水代码是有问题的,当网关所有者不知道这些类型的,当它是不现实的产品团队拥有这些基础设施之间的关系。
Apollo Platform的解决方案是一种联合开发范例,该范例使用自定义指令以声明方式将类型链接在一起。它消除了网关中对粘合代码的需求,设置了适当的关注点分离,并允许您扩展在本地开发中不拥有的类型。看看公司是否会采用它以及联盟将如何影响GraphQL规范将很有趣。
如果构建一张图不切实际怎么办?在贝宝,我们为快速迭代和持续学习进行了优化。许多开发人员没有动力放慢脚步,达成共识并成为更大整体的一部分。这是我们文化的双刃剑。如果无法绘制一张图,我们还可以如何重用工作?另一个选项是带有GraphQL Modulesgraphql-component之类的选项的本地模块。使用本地模块,您可以选择所需的类型和字段,并且所有代码都在同一进程中运行。模式缝合和联合的许多问题都消失了。但是,我们有大量类似的端点,并且服务器的占用空间并未减少。这个可以吗?
这是我们使用GraphQL遇到的最困难的问题。

(banq注:业务聚合问题能通过GraphQL反复组合解决吗?)