介绍:GraphQL服务端开发

本文属使用Prisma构建GraphQL服务系列。

每个GraphQL API的核心:GraphQL schema

schema中的类型定义了API操作

每个GraphQL API的核心是GraphQL schema,它清楚地定义了所有可用的API操作和数据类型。schema使用称为模式定义语言(Schema Define Language,SDL)的专用语法编写。 SDL简洁易用。

下面是一个演示如何定义简单的User类型的例子,该User有两个字段,即idname

type User {
  id: ID!
  name: String!
}

每个GraphQL schema都有三种特殊的根类型,称为查询(Query),突变(Mutation)和订阅(Subscription)。这些根类型的字段定义了API接受的操作。

作为示例,请考虑以下Query和Mutation类型:

type Query {
  users: [User!]!
}

type Mutation {
  createUser(name: String!): User!
}

此schema定义的GraphQL API将允许执行以下两项操作:

# 查询用户列表
query {
  users {
    id
    name
  }
}

# 创建新用户
mutation {
  createUser(name: "Sarah") {
    id
  }
}

查询(Query) / 突变(Mutation)内的所有字段及其参数的集合称为操作的选择集(selection set)。

根字段定义API的入口点(entry-points)

根类型上的字段也称为根字段(root fields),并为API提供入口点(entry-points)。这意味着发送到API的查询(Query) / 突变(Mutation)总是需要以其中一个根字段开始。

根字段的类型(type)确定哪些字段可以进一步包含在查询的选择集中。在上面的例子中,类型是User[User!]!在这两种情况下都允许包含User类型的任何字段。

如果根字段具有标量(scalar)类型,则不可能在选择集中包含任何其他字段。作为示例,请考虑以下GraphQL schema:

type Query {
  hello: String!
}

此查询定义的GraphQL API只接受一个hello操作:

query {
  hello
}

解析器函数(Resolver functions)实现schema

GraphQL服务中的结构与行为

GraphQL具有明确分离的结构(structure)和行为(behaviour)。虽然SDL schema定义仅描述了API的抽象结构(abstract structure),但具体实现是通过所谓的解析器功能(Resolver functions)实现的。schema定义和解析器(resolver)实现的组合通常被称为可执行schema(executable schema)。

GraphQL schema中的每个字段都由一个解析器函数支持,这意味着解析器函数与GraphQL schema中的字段一样多(包括除根类型之外的其他类型的字段)。

他为某个领域的解析器功能负责为该领域准确提取数据。例如,上面的用户root字段的解析器知道如何获取用户列表。

字段的解析器函数负责为该字段准确提取数据。例如,上面的user 根字段的解析器知道如何获取用户列表。

因此,GraphQL查询解析的过程其实是调用查询中包含的字段的解析器函数的操作,因为每个解析器都会为其字段返回数据。

解析器函数(Resolver functions)剖析

解析器函数总是需要四个参数(按以下顺序):

  • parent (有时也称为root) :查询由GraphQL引擎解析,该引擎调用查询中包含的字段的解析器。因为查询可以包含嵌套字段,所以可能会有多级解析器执行。parent 参数始终表示前一个解析器调用的返回值。请参阅此处了解更多信息。
  • args:为该字段提供的潜在参数(例如,上面的createUser突变示例中的name)。
  • context:上下文,每个解析器都可以写入和读取的解析器链(基本上是解析器交流和共享信息的方法)的对象。
  • info:查询或变异的AST表示。您可以在这篇文章中了解更多信息:Demystifying the info Argument in GraphQL Resolvers.

以下是我们如何实现上述模式定义的解析器的一种可能方式(实现假定有一些全局对象数据库提供了与数据库的接口):

const Query = {
  users: (parent, args, context, info) => {
    return db.users()
  }
}

const Mutation = {
  createUser: (parent, args, context, info) => {
    return db.createUser(args.name)
  }
}

const User = {
  id: (parent, args, context, info) => parent.id,
  name: (parent, args, context, info) => parent.name,
}

上面的示例schema定义恰好具有四个字段。此解析器实现提供了四个相应的解析器功能。请注意,User类型的解析器实际上可以省略,因为它们的实现是微不足道的,并且由GraphQL执行引擎推断。

Prisma如何帮助开发GraphQL服务

构建GraphQL服务的难点在于实现解析器(resolvers)

如上所见,实现GraphQL服务器的主要开发任务围绕着定义schema并实现相应的解析器(resolver)功能。这也被称为schema驱动开发(schema-driven development,SDD)。

当实现解析器功能时,您需要选择某种数据源,为查询响应部分时获取的数据。这个数据源可以是任何东西 - 它可能是一个数据库(SQL或NoSQL),一个REST API,一些第三方服务或任何类型的遗留系统。

上面的例子很简单,并假设全局数据库对象的可用性为一些数据源提供了一个简单的接口。实际上,您可能会遇到更复杂的场景。特别是因为GraphQL查询可以深度嵌套,将它们转换为SQL(或其他数据库API)非常麻烦并且容易出错。

Prisma让解析器变得简单明了

使用Prisma时,一般认为解析器只是将传入查询的执行委托给底层的Prisma引擎,而不是直接访问数据库。因此,大多数解析器实现将是简单的单线程。将传入查询解释为数据库API的工作由Prisma完成

使用GraphQL bindings搞定这个事情,允许通过调用JavaScript中的专用函数(或任何其他用于后端开发的编程语言)与Prisma GraphQL API交互。如此,解析器实现变得与上面的模拟db示例一样简单。

GraphQL bindings - 更好的 ORM

绑定允许通过调用编程语言中的函数来发送查询和突变

GraphQL bindings,在某种程度上与传统的ORM有点像。他们都允许通过调用编程语言中的函数来与GraphQL API通讯,而不是构建直接发送给API的原始查询字符串。

像上面的createUser突变。无论何时您想将其发送到GraphQL API,您都需要像这样拼出整个突变:

mutation {
  createUser(name: "Sarah") {
    id
  }
}

然后你将这个字符串放入HTTP POST请求的body并将它发送到API,这样的缺点是查询被表示为字符串。这抹杀了GraphQL的核心优势之一:强大的类型系统!基于字符串的方法完全没有发挥强类型API的优势。

GraphQL bindings通过允许您调用专用函数来向API发送查询和突变,而不是通过手动构建字符串并通过HTTP将其发送到服务端。这些函数以您的GraphQL schema的根字段命名。

通过绑定,上面的createUser突变可以通过调用相同名称的函数发送到服务端:

binding.mutation.createUser({ name: "Sarah" }, '{ id }')

同样,你可以将上面的users查询转换为函数调用:

binding.query.users({}, '{ id name }')

如你所见,这些函数调用中的第一个参数是一个携带查询(query)/突变(mutation)参数(args)的对象,第二个参数是决定哪些数据应该包含在响应中的选择集(selection set)。

调用这些方法时,引擎中的binding实例负责将操作转换为GraphQL查询,并将查询发送到服务端,并将响应作为编程语言中的对象返回。

静态与动态绑定(Static vs Dynamic Bindings)

绑定可以使用两种风格:静态(Static)和动态(Dynamic)。

静态绑定(Static bindings )用于与来自静态和强类型编程语言(如TypeScript或Scala)的GraphQL API进行交互时,在编译时需要知道所有表达式的类型。在这种情况下,绑定函数在构建时生成(使用代码生成)。因此,这些绑定函数的所有调用都可以通过编译器进行验证,并且拼写错误以及结构错误(如错误类型的传递参数)在编译时被捕获。

另一个优势是您的编辑器现在可以帮助您创建API请求,例如自动完成可用的操作和查询参数!这样就改变了后端开发游戏的玩法,并将开发者体验提升到一个新的水平。没有SQL字符串或其他脆弱的数据库API - 幸亏有了Prisma绑定使你可以用强类型层与数据库进行交互!

动态绑定(Dynamic bindings )通常用于动态编程语言(如JavaScript)。它们不需要额外的构建步骤(静态绑定就是如此)。绑定实例上的方法调用仅在运行时转换为GraphQL查询。这仍然提供了使用简洁和简单的绑定语法的主要好处。使用适当的构建工具仍然可以实现构建时错误检查和自动完成等优势。

架构入门:两个GraphQL API层

在使用Prisma构建GraphQL服务器时,您需要处理两个GraphQL API:

  • database layer:由Prisma负责的数据库层
  • application layer:应用层负责与写入或读取数据库数据无关的任何功能(如业务逻辑,身份验证和权限,第三方集成等)。

数据库层完全通过prisma.yml进行配置,并使用Prisma CLI进行管理。应用层是您正在用自己喜欢的编程语言实现的GraphQL服务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,348评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,122评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,936评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,427评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,467评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,785评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,931评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,696评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,141评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,483评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,625评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,291评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,892评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,492评论 2 348

推荐阅读更多精彩内容

  • 本文属使用Prisma构建GraphQL服务系列。 当搞定了GraphQL服务端开发,且经过充分测试,那么接着需要...
    guog阅读 2,633评论 0 1
  • 本文属使用Prisma构建GraphQL服务系列。 本教程学习如何使用Prisma对数据库生成GraphQL AP...
    guog阅读 7,238评论 2 3
  • 本文属使用Prisma构建GraphQL服务系列。 本文介绍如何使用typescript开发prisma服务。将使...
    guog阅读 2,900评论 0 2
  • 本文属使用Prisma构建GraphQL服务系列。 在本教程中,您将学习如何在使用Prisma和graphql-y...
    guog阅读 1,206评论 1 0
  • 本文属使用Prisma构建GraphQL服务系列。 要理解Prisma是什么以及它是如何工作的,至关重要的是你对G...
    guog阅读 7,802评论 0 1