Nexus 1.0:类型安全,代码优先GraphQL API的主要版本

2020-12-15 16:44:01

Nexus是最初由Tim Griesser创作的库,它使开发人员可以构建代码优先和类型安全的GraphQL API。 Prisma两年来一直是该库的核心贡献者,并帮助塑造了它的发展。

此版本是杰出社区反馈和贡献的结晶,是Nexus在生产中经过多年的实战测试的结果,也是对为那些为GraphQL API的开发人员创造出色的开发人员经验的经验教训的回应。

注意:Prisma的产品不再以GraphQL为中心。您可以在REST API,GraphQL API或要在Node或Go中访问数据库的任何其他设置中使用Prisma。尽管我们为Nexus做出了贡献,但没有必要将其与Prisma一起使用。了解有关Prisma提供的产品以及如何使用它的更多信息。

Nexus提供了一种在Node中构建代码优先的GraphQL API的方法。与建立优先的GraphQL API相比,代码优先的方法与模式优先的方法(可能更常见)形成了对比。

大多数刚建立GraphQL API的开发人员都是从采用模式优先的方法开始的,这种方法已被Apollo及其Apollo Server等公司所普及。

使用模式优先方法,编写GraphQL API需要一组类型定义和随附的解析器。一个简单的模式优先服务器可能看起来像这样:

const Query = {posts:()=> [{id:' 1' ,标题:“我的第一台GraphQL服务器” ,正文:我如何编写我的第一个GraphQL服务器' ,},],}

尽管模式优先方法很容易上手,但它具有一些固有的缺陷,这些缺陷会在应用程序开始变大时使开发变得困难。

Nexus采用了另一种方法来构建GraphQL API。使用Nexus,可以将模式和解析器写在同一位置,而不是使用单独的架构和一组解析器。

从' nexus'导入{objectType,queryType,makeSchema} const Post = objectType({name:' Post',definition(t){t。id(' id')t。string(' title')t。字符串(' body'}})const Query = queryType({定义(t){t .list。字段(' posts',{resolve:()=> [ {id:' 1&#39 ;,标题:'我的第一个GraphQL服务器'正文:'我如何编写我的第一个GraphQL服务器',},],})}} ,})const schema = makeSchema({类型:[发布,查询],})

在构建模式优先的GraphQL API时,通常首先将所有类型定义和解析器放在单个文件中来开始。当模式和解析器彼此相邻时,同时在两者中同时工作非常简单。

但是,随着应用程序的增长,最经常需要将模式的各个部分移动到它们自己的单独模块和文件中。此时,使用GraphQL API变得更加乏味。通过这种模块化,需要在模式定义语言和JavaScript / TypeScript之间来回切换以编写解析器。不仅需要不断地在文件之间切换,而且还需要在上下文上进行上下文切换以在两种语言之间工作。

使用Nexus,我们的架构及其解析器总是一起定义的。 Nexus还允许我们用通用语言编写所有内容。这使我们可以完全避开共址/上下文切换问题,即使我们的应用程序变得很大,也可以帮助我们提高生产力。

使用Nexus的主要好处之一是能够自动生成TypeScript类型和GraphQL架构定义语言(SDL)文件。生成的类型对于在为GraphQL API提供动力的代码中增加额外的类型安全性很有用。生成的SDL文件可用于许多目的。例如,我们可以配置我们的编辑器以了解我们API的形状,从而使我们能够对所编写的查询和变异进行自省。

Nexus是免费提供类型和SDL生成的,可以通过在makeSchema调用中提供一些配置来启用它。

从' path'导入路径const schema = makeSchema({类型:[Post,Query],输出:{schema:path。join(__dirname,' Generated / schema.gen.graphql'),typegen:path。join(__dirname,& #39; generate / nexusTypes.gen.ts'),},})

Nexus 1.0进行了许多更改。阅读完整的变更日志,然后按照下面的步骤查看新功能!

Nexus 1.0现已在连结套件名称下提供。现在,所有导入都来自于nexus,而不是@ nexus / schema。

在Nexus的早期版本中,默认情况下将字段视为不可为空。不同于其他GraphQL API框架,除非另有说明,否则该框架会将字段视为可为空。 Nexus作者之所以采用这种方法,是因为默认情况下允许字段不可为空会给API开发带来一些长期风险。

字段的默认可为空性现已在Nexus中被颠倒,以便我们能够与GraphQL最佳做法和期望保持一致,特别是GraphQL的作者。

上面的Post对象类型的代码使用称为nonNull的属性,该属性为指定字段应为非null提供了更大的灵活性。对于链API不太理想的情况,例如表达深度嵌套的类型以及以编程方式创建非空类型的情况,它很有用。

从' nexus'导入{queryType,stringArg,nonNull} queryType({definition(t){t。field(' tags',{type:nonNull(' String')// => String!args:{id:nonNull(stringArg ())//或nonNull(' String')=> String!},resolve(){// ...}}}}}))

nonNull函数接受一个参数,该参数可用于指定类型或参数的类型。

现在,默认情况下,字段可以为空,但可以全局更改此行为,也可以在Nexus API的类型级别更改此行为。

queryType({nonNullDefaults:{output:true,},定义(t){t。string(' echo',{args:{message:' String',},resolve(_root ,args){return args .message},}}},})

在此示例中,Nexus现在期望所有查询类型字段的字段都不应为空。

如果您选择将类型默认更改为非null,则可以使用nullable函数来指定某些字段应该为null。

从' nexus'导入{queryType,stringArg,nullable} queryType({nonNullDefaults:{input:true,output:true,},definition(t){t。field(' echo',{type:nullable(' String'),args :{消息:可为null(stringArg()),},解析(_root,args){return args .message},}}}})

要了解有关Nexus如何处理可空性(包括与API进行交互的其他方式)的更多信息,请阅读可空性指南。

Nexus 1.0引入了一项用于处理列表类型的新功能。列表函数可以应用于输入和输出,类似于nonNull和nullable函数的用法。

从' nexus'导入{queryType,stringArg,list} queryType({definition(t){t。field(' tags',{type:list(' String')//-> [String] args:{ids:list( stringArg())//或list(' String')-> [String]},resolve(){// ...}}}}})

仍然保留了用于创建列表的相同链接API,但存在列表功能可帮助您在链接不理想的情况下提供帮助。

联合类型允许您表达成员类型可以完全不同的多态字段。

接口类型使您可以表达多态字段,其中字段可以返回许多不同的对象类型,但是它们都共享字段的某些子集。

官方的GraphQL JavaScript软件包支持三种用于实现Abstract类型的策略。 Nexus 1.0现在提供了用于实施这三种策略的API,并始终提供类型安全性。

请注意,以下示例适用于联合类型,但对于接口类型也同样适用。

集中式策略允许您以集中式(针对联合类型)区分工会成员类型。例如:

const SearchResult = unionType({name:' SearchResult',resolveType(data){const __typename = data .album?' Song':data .rating?' Movie' :data .width?' Photo':如果(!__ typename){抛出新错误(`无法解析传递给并集类型" SearchResult"`的数据类型)}返回__typename} ,定义(t){t。成员(' Photo',' Movie',' Song')}})

判别模型字段(DMF)策略允许您以潜在的模块化方式来区分工会成员类型。它基于在类型为抽象类型的字段的解析程序返回的模型数据中提供__typename字段。这是一个例子:

const Query = queryType({definition(t){t。field(' search',{type:' SearchResult',args:{pattern:stringArg(),},resolve(root ,args,ctx){返回ctx .db。搜索(args .pattern)。map(result => {const __typename = result .album?' Song':result .rating?' Movie& #39 ;:结果.width?' Photo&#39 ;:如果if(!__ typename){抛出新错误(无法解析传递给并集类型" SearchResult"的数据类型))}返回{...结果,__typename,}}}},}}}})

模块化策略使您能够以模块化方式(惊奇)来区分工会成员类型。它使用您实现的谓词功能,使Nexus(实际上是GraphQL.js)在运行时知道发送到客户端的数据是否属于相应类型。这是一个例子:

const Movie = objectType({name:' Movie',isTypeOf(data){返回布尔值(data .rating)},定义(t){t。字符串(' url')t 。field(' rating',{type:' MovieRating'}}}})const Photo = objectType({name:' Photo&#39 ;, isTypeOf(data ){return Boolean(data .width)},定义(t){t。字符串(' url')t。int(' width')t。int(' height'}}})const Song = objectType({name:' Song',isTypeOf(data){return Boolean(data .album)}},定义(t){t。字符串(& #39; url')t。字符串(' album')}})

阅读抽象类型指南,详细了解策略如何工作,启用或禁用,类型安全等。

第一次开始实现模式时,您会发现一开始可能并不明显。客户端在数据图中看到的数据与流经用于实现该图的内部解析器的数据不同。客户端可以看到API类型,但API作者需要处理其他内容,通常称为" backing"或" root"在Nexus中输入类型。

在Nexus 1.0上," backing"和" root"现在,这些类型在全球范围内被称为"来源类型"。

阅读源代码类型指南,以了解有关它们是什么以及如何使用它们的更多信息。

Nexus 1.0提供了更好的文档体验,包括更多指南,更好的导航,JSDoc支持以及对Nexus Playground的升级。

Nexus游乐场已转换为一组准备好的Codesandbox示例。新的Playground提供了现成的Nexus API,您可以将其扩展和扩展。

Nexus 1.0是该库发展过程中的重要里程碑。在过去两年多的时间里,社区在社区的形成和成功中发挥了至关重要的作用。我们要感谢所有试用Nexus,提交问题和提出请求并将其投入生产的人!

在Twitter上关注@nexusgql并观看回购,以获取有关Nexus情况的未来更新。