-
注:本文稿基于Will Lyon在2019年10月的NODES 2019上发表的演讲整理修改后发表。
1、GRAND是什么?
GRANDstack是一个开发基于Neo4j图数据库应用的全栈框架,它基于GraphQL、React、Apollo和Neo4j Database 构建数据库应用程序。GraphQL是API层;React是一个JavaScript UI库,用于在Web、移动以及VR中创建用户界面;Apollo是一套工具和服务,它使在客户端和服务器上使用GraphQL更加容易;最后,Neo4j数据库是原生图数据库。
GraphQL是一种API查询语言,虽然它的名称里有Graph,其实它和图数据库没有关系;而且虽然它的名称里面有QL,它也不是一种数据库查询语言。GraphQL基于一个类型系统(Type Script),用来描述API中可用的数据,客户端应用仅请求相关数据,并且数据可以与查询相同的格式返回。
相比现有的RestfulAPI,GraphQL大大简化了API的定义和部署:仅需要简单几个GraphQL API/URL就可以描述Web服务提供的对多种数据内容的不同操作。使用类型系统的好处是把访问的数据的细节封装在了请求里面、而不是通过URL来区分。
GraphQL将应用的数据看做是一个“图”,即由关系连接起来的实体/节点。这也是它名称中Graph的来由。对于像Neo4j这样的原生图数据库来说,GraphQL有着特别的“亲切感”,因为数据库的模式和应用API的模式是一致的、而且无需转换。
GRAND Stack的目标是使开发基于Neo4j图数据库的API变得容易。通过GraphQL类型定义来驱动数据库数据模型来做到这一点,根据这些类型定义自动生成GraphQL CRUD API并自动生成解析器,所有步骤都无需手动编写相关代码。反过来也是一样:对于现有的Neo4j数据库,我们可以从数据库中推断出GraphQL模式,无需编写什么代码即可在Neo4j之上提供完整的CRUD GraphQL API。这就是GRAND Stack和GraphQL Neo4j的核心所在。
有关GRAND Stack的所有信息和文档,都可以在这里找到。
2、Web应用开发的演变
早在90年代中期,开发Web应用使用CGI (Common Gateway Interface,很奇怪的名字吧)程序。CGI程序保存在服务器的目录中,他们通常是可执行脚本,在服务器上运行以从数据库中获取数据并在网页上显示动态内容。这样,我们将数据库查询、静态HTML和其他内容嵌入模板语言中。
最终所有这些都变成了LAMP堆栈,即Linux、Apache、MySQL和PHP。它曾经非常流行,因为它使得从数据库动态获取数据并呈现简单视图变得非常容易。
然而,我们逐渐地意识到在模板语言中嵌入数据库查询很丑陋且难以维护,因此我们需要一种更好的表示–一个中间层。这是REST API和JSON开始流行的时候,jQuery也成为一种流行的从REST API获取数据并将其呈现在前端的方法。
在那之后,我们看到能支持Web规模的数据库的需求,因此有了NoSQL和MEAN堆栈的出现,也就是Mango、Express、Angular和Node.js。它们使得将文档数据库中的文档映射到REST API变得很容易。我们还开始看到像Angular这样的前端框架,它使我们更容易封装逻辑和UI。而Meteor则使得实现近乎实时的流内容得Web应用开发成为可能。
今天,在前端最新的发展是React框架,它于2013年由Facebook开源。由此,我们完成了从对事件的侦听(event listener)发展到对状态的声明性操作(declarative actions about the state)的目标。
类似虚拟DOM等概念提高了性能。现在的web应用中,我们无需渲染整个视图,而仅需要根据状态变化来渲染所需的最小页面内容。这不仅提供了封装逻辑和UI的组件,而且还为我们提供了性能优化。
此后不久,GraphQL也被Facebook开源。同样的,这都是关于有效的数据获取并为数据提供类型系统,从而使我们能够以图的形式描述和查询API(是API、而不是数据)。
大约在同一时间,我们看到了诸如Neo4j之类的图数据库(Graph Database)的兴起。随着从其他形式的NoSQL数据库实现迁移越来越成为问题,并且数据变得更加复杂,图数据库的直观图数据模型对开发人员更具吸引力。图数据库能够提供的性能改进大不相同,而且已经过特别优化、可以最有效地遍历图结构的数据。
将所有这些与我们今天使用的无服务器(serverless)和各种部署选项结合起来,诸如ZEIT Now和Netlify之类的技术通过简单的命令行工具就能轻松部署前端和后端应用代码。
如果我们回顾曾经出现过的技术,就会发现图实际上无处不在(Graphs are everywhere)。使用GraphQL时,我们讨论的是将API发布为图的结构(关联的数据);在图数据库中,我们谈论的是使用图数据模型实际存储数据到数据库中。
3、GraphQL概述
GraphQL首先是一种模式定义语言。例如,如果我们要定义一个关于电影-导演-演员的数据模型,可以用GraphQL这样描述:
当我们把上面的模式定义发布到GraphQL服务端(例如Apollo)后,GraphQL的“自省”(Introspect)功能会自动生成并发布一系列标准查询和数据库操作功能。如果用GraphiQL和GraphQL Playground之类的工具,可以看到类似下面的服务接口:
如果用GraphQL来查询这个电影数据库,搜索名称是“A River Runs Through It.”的影片,我们只要把下面的参数传递给API就可以了:
除了安装名称查询影片,这个例子中还从找到的影片,关联到相关的演员、导演,并返回演员和导演的姓名(name);特别的,对于导演,还会继续关联查询到三部由同一导演拍摄的电影标题。在GraphQL的查询中,数据实体和它们之间的关系构成一个“图”(Graph)的结构,即由关系/边连接起来的节点/实体/顶点集合,这也是GraphQL的名称来源。
你应该已经想到了,我们正在执行的是“图遍历”:从一部电影出发,通过演员、导演和与导演相关的所有电影进行遍历搜索,并返回下面的结果:
GraphQL是一种API查询语言、而不是一种数据库查询语言。特别的,对于API背后访问的实际数据库及其类型,GraphQL其实并不关心。因此,不仅仅是Neo4j图数据库,像关系数据库、文档数据库、键-值对存储、列式数据库,都可以用GraphQL来访问。
4、GraphQL的优势
除了简化API的发布和管理,GraphQL还有其他优点。其一是高效的数据获取,也就是说不会过度获取数据。传统的RestfulAPI需要从后端请求比实际所需更多的数据,并通过网络传送。使用GraphQL,我们只请求并返回感兴趣的一部分数据对象。
数据提取不足是GraphQL解决的另一个问题,在单个请求中可以发送呈现视图所需的所有数据。使用RESTful API,如果要获取一个博客文章列表,并且要包括作者等信息,那么可能不得不对文章的每个作者发送另一个API请求。但是,使用GraphQL,我们可以在一个请求中获得所有帖子和作者的信息。
其实,从前端框架到API,到处都有图结构数据的存在。GraphQL使我们可以进行更多基于组件的数据交互,而且在关系而不仅是资源的上下文中进行。
5、构建GraphQL服务
如何构建GraphQL服务呢?本质上,高级的方法是采用定义并实现解析器类resolver,在这个类中定义如何获取和解析GraphQL请求的数据的功能。
标准的实现中,还必须进行加入授权验证。为了支持对数据库的各种查询,并将数据结果发送回去,这个解析器中最终会需要编写很多模板,使得类越来越复杂。另外,这也相当于要在数据库和中间层维护两套数据模式。
为了简化对模式的定义、API的定义,出现了一些GraphQL引擎。
6、GraphQL引擎
GraphQL引擎可以自动生成GraphQL的模式,并根据GrahQL请求生成数据库查询。下面是一些流行的GraphQL引擎,它们多数使用Postgres存储元数据,或者在AWS AppSync的基础上,通过GraphQL公开AWS资源。GRAND Stack中的Apollo是一个GraphQL引擎,它运行在Node.js服务器上。
7、集成Neo4j和GraphQL
详细的Neo4j和GraphQL集成有完整并且免费的教程可以学习,这里我们主要看一下重要的过程和特性。
7.1 使用TypeScript定义模式
我们使用TypeScript描述Neo4j中的图模式,包括节点、及其属性;节点之间的关系通过代表节点的类型的子对象(集合)来表示,例如下面例子中的Actor类型中的movies: [Movie]。
7.2 自动生成GraphQL CRUD API
当类型定义完成后,可以自动生成关于类型中对象、关系和属性的CRUD操作的API。在Apollo中可以直接测试这些API:下图左边的窗口是请求的JSON格式内容,右边是返回结果。
7.3 用Cypher扩展GraphQL
在模式中通过定义Query对象,可以将复杂的Cypher查询和GraphQL API绑定:
7.4 Neo4j GraphQL
Neo4j和GraphQL的集成有几种方式,例如运行在数据库端的扩展插件,这里要介绍的是neo4j-graphql.js,它可以部署在Node.js服务中、和其他Javascript GraphQL工具,例如Apollo Server GraphQL.js一起使用。
8、小结
本文中我们对GRAND 全栈框架做了概要的介绍,并简单的说明了GraphQL的类型/模式定义、API操作的自动生成,以及Neo4j GraphQL集成的方法。
关于完整的GRAND开发方法和实例,可以参见这里,也可以从这里下载GraphQL 应用样例。