当前位置: 首页 > 后端技术 > Node.js

GraphQL基础

时间:2023-04-03 16:15:12 Node.js

GraphQL基础——查询和更改字段(Fields)GraphQL请求对象上的特定字段,例如://Request{hero{namefriends{name}}}//Response{"data":{"hero":{"name":"R2-D2","friends":[{"name":"LukeSkywalker"},{"name":"HanSolo"}]}}}我们可以发现,查询和结果具有相同的结构和字段。这是GraphQL最重要的特性,因为通过这种方式,你总能得到你想要的数据,而且服务器确切地知道客户端请求了哪些字段。参数(Arguments)在请求中传递参数是很有必要的,也可以让查询更加灵活方便。当然,GraphQL也可以做到。//请求{human(id:"1000"){nameheight}}//响应{"data":{"human":{"name":"LukeSkywalker","height":1.72}}}alias(Aliases)当返回的字段名需要重命名时,别名可以发挥作用。在下面的示例中,hero可以替换为empireHero和jediHero。//请求{empireHero:hero(episode:EMPIRE){name}jediHero:hero(episode:JEDI){name}}//响应{"data":{"empireHero":{"name":"LukeSkywalker"},"jediHero":{"name":"R2-D2"}}}片段(Fragments)当我们的请求中有重复的字段需要展示时,单独列出可能会显得臃肿,这也是GraphQL包含的原因一个称为片段的可重用单元,以下示例显示如何使用片段来解决上述场景://request{leftComparison:hero(episode:EMPIRE){...comparisonFields}rightComparison:hero(episode:JEDI){...comparisonFields}}fragmentcomparisonFieldsonCharacter{nameappearsInfriends{name}}//response{"data":{"leftComparison":{"name":"LukeSkywalker","appearsIn":["NEWHOPE","EMPIRE","JEDI"],"friends":[{"name":"HanSolo"}]},"rightComparison":{"name":"R2-D2","appearsIn":["NEWHOPE","EMPIRE","JEDI"],"friends":[{"name":"LeiaOrgana"}]}}}有了片段,我们的代码可以大大简化。片段的概念通常用于将复杂的应用程序数据需求拆分成小块,尤其是当您将来自不同片段的大量UI组件组合到一个初始数据提取中时。操作名称(Operationname)在此之前,我们都是使用速记语法,省略了查询关键字和查询名称,但是在生产中使用操作类型和操作名称可以让我们的代码不那么歧义。下面的示例包括关键字查询作为操作类型和操作名称HeroNameAndFriends://请求查询HeroNameAndFriends{hero{namefriends{name}}}//response{"data":{"hero":{"name":"R2-D2","friends":[{"name":"LukeSkywalker"},{"name":"LeiaOrgana"}]}}}操作类型可以是查询,变异或订阅,描述你打算做什么类型的操作。操作类型是必需的,除非您使用查询速记语法,在这种情况下您不能为操作提供名称或变量定义。操作名称是您的操作的有意义且明确的名称。变量(Variables)到目前为止,我们已经在查询字符串中写入了参数。但是在很多应用中,字段参数可能是动态的,如下面的例子,这样可以让我们的请求更加灵活。//定义变量{"episode":"JEDI"}//请求查询HeroNameAndFriends($episode:Episode){hero(episode:$episode){namefriends{name}}}变量定义(Variabledefinitions)变量定义看起来像($episode:Episode)在上面的查询中。它的工作方式与类型化语言中函数的参数定义相同。它列出了所有变量,这些变量必须以$为前缀,后跟它们的类型,在本例中为Episode。变量定义可以是可选的或必需的。在上面的例子中,没有!在Episode之后,所以它是可选的。但是如果你传递变量的字段需要非空参数,那么这个变量肯定是必须的。默认变量(Defaultvariables)可以通过在查询中的类型定义后追加默认值来为变量赋默认值。queryHeroNameAndFriends($episode:Episode="JEDI"){hero(episode:$episode){namefriends{name}}}指令(Directives)我们可能需要使用变量来动态改变我们查询的结构,如以下示例://定义变量{"episode":"JEDI","withFriends":false}//请求查询Hero($episode:Episode,$withFriends:Boolean!){hero(episode:$episode){namefriends@include(if:$withFriends){name}}}//response{"data":{"hero":{"name":"R2-D2"}}}我们在GraphQL中使用了一个称为指令的新特性。指令可以附加到字段或片段中包含的字段。GraphQL的核心规范包含两个指令:@include(if:Boolean)仅当参数为真时才包含此字段。@skip(if:Boolean)如果为真,则跳过该字段。突变GraphQL建立了一个约定,即任何写入操作都应通过突变显式发送。//定义变量{"ep":"JEDI","review":{"stars":5,"commentary":"Thisisagreatmovie!"}}//更改请求突变CreateReviewForEpisode($ep:Episode!,$review:ReviewInput!){createReview(episode:$ep,review:$review){starscommentary}}//Response{"data":{"createReview":{"stars":5,"commentary":"这是一部很棒的电影!}}}一个更改也可以包含多个字段,就像查询一样。除了名称之外,query和mutation之间的一个重要区别是,当查询字段时,它们是并行执行的,而当字段发生变化时,它们是线性执行的,一个接一个。这意味着如果我们在一个请求中发送两个incrementCredits突变,第一个保证在第二个之前执行,以确保我们没有竞争条件。内联片段(InlineFragments)如果你查询的字段返回一个接口或联合类型,那么你可能需要使用内联片段来检索底层具体类型的数据://Definevariables{"ep":"JEDI"}//请求查询HeroForEpisode($ep:Episode!){hero(episode:$ep){name...onDroid{primaryFunction}...onHuman{height}}}//response{"data":{"hero":{"name":"R2-D2","primaryFunction":"Astromech"}}}在此查询中,英雄字段返回角色类型,根据剧集参数可能是人类或机器人。directselect的情况下,只能请求Character上存在的字段,比如name。如果要请求特定类型的字段,则需要使用类型条件内联片段。因为第一个片段被标记为...在Droid上,只有当hero返回的Character是Droid类型时,primaryFunction才会被执行。Human类型的高度字段也是如此。命名片段也可以在相同的情况下使用,因为命名片段总是有一个附加到它们的类型。元字段(Metafields)在某些情况下,你不知道你将从GraphQL服务中得到什么类型,这时候你需要一些方法来决定如何在客户端处理数据。GraphQL允许您在查询中的任何点请求元字段__typename以获取该点的对象类型名称。//Request{search(text:"an"){__typename...onHuman{name}...onDroid{name}...onStarship{name}}}//Response{"data":{"search":[{"__typename":"Human","name":"HanSolo"},{"__typename":"Human","name":"LeiaOrgana"},{"__typename":"Starship","name":"TIEAdvancedx1"}]}}在上面的查询中,搜索返回一个联合类型,它可能是三个选项之一。如果没有__typename字段,几乎不可能在客户端区分这三种不同的类型。参考资料:https://graphql.cn/learn/quer...