14个文本转图像AI API
Nexus 1.0:面向类型安全、代码优先GraphQL API的重大版本发布
Prisma 是 Nexus 的核心贡献者,Nexus 是一个用于构建代码优先和类型安全的 GraphQL API 的库。近期,Nexus成功发布了1.0版本。本文旨在回顾Nexus的基本概念、它所提供的价值,以及1.0版本中引入的新功能。
Nexus 是一个最初由 Tim Griesser 编写的库,它允许开发人员构建代码优先和类型安全的 GraphQL API。在过去的两年多时间里,Prisma 作为该库的核心贡献者,一直在推动其发展与成型。
今天,我们非常荣幸地宣布 Nexus 1.0 版本的发布。
这一版本是社区宝贵反馈与贡献、多年实战生产中 Nexus 的经验积累,以及致力于为 GraphQL API 构建者打造卓越开发体验所提炼出的结晶。
请注意:Prisma 的产品已不再局限于 GraphQL。您可以在 REST API、GraphQL API 中,或任何需要在 Node 或 Go 中访问数据库的场景下使用 Prisma。尽管我们为 Nexus 做出了贡献,但它并不必须与 Prisma 配套使用。更多关于 Prisma 提供的功能及其使用方法的信息,请查阅相关文档。
什么是 Nexus?
Nexus提供了一种在Node.js环境中构建代码优先GraphQL API的途径。这种方法与(可能更为普遍的)架构优先方法形成了鲜明的对比。
许多刚开始构建GraphQL API的开发人员通常会首先选择架构优先的方法,这种方法已被诸如Apollo及其Apollo Server产品等公司广泛推广。
在采用架构优先的方法时,构建GraphQL API需要定义一组类型以及相应的解析器。一个简单的架构优先服务器可能呈现如下形式:
type Post {
id: ID!
title: String!
body: String!
}
type Query {
posts: [Post]!
}
const Query = {
posts: () => [
{
id: '1',
title: 'My first GraphQL server',
body: 'How I wrote my first GraphQL server',
},
],
}
虽然架构优先方法很容易上手,但它也存在一些固有的缺点,随着应用程序规模的扩大,这些缺陷可能会让开发工作变得棘手。
Nexus则采用了截然不同的方法来构建GraphQL API。在使用Nexus时,架构和解析器是通过代码在同一位置编写的,而不是将它们分别保存在独立的架构和解析器集合中。
如果将上面的Post示例用Nexus进行重构,将会呈现如下形式:
import { objectType, queryType, makeSchema } from 'nexus'
const Post = objectType({
name: 'Post',
definition(t) {
t.id('id')
t.string('title')
t.string('body')
},
})
const Query = queryType({
definition(t) {
t.list.field('posts', {
resolve: () => [
{
id: '1',
title: 'My first GraphQL server',
body: 'How I wrote my first GraphQL server',
},
],
})
},
})
const schema = makeSchema({
types: [Post, Query],
})
使用 Nexus 采用代码优先方法有很多好处,包括:
- 架构和解析程序共存
- SDL 和类型生成
- 无需额外的工具
架构和解析程序共存
在构建以架构为核心的 GraphQL API 时,一个常见的做法是起初将所有类型定义与解析器置于同一文件中。当架构与解析器相邻配置时,同时在两者间进行操作显得相对直观便捷。
然而,随着应用程序规模的扩大,通常需要将架构的不同部分拆分到各自的独立模块和文件中。正是在这一环节,GraphQL API 的处理变得更为繁琐。这种模块化操作要求在 GraphQL Schema Definition Language(SDL)与 JavaScript/TypeScript 之间频繁切换以编写解析器。这不仅仅意味着需要在多个文件间不断跳转,还需要在心理上进行上下文转换,以适应在两种不同语言间的工作。
借助 Nexus,我们的架构及其解析器总是协同定义。Nexus 还允许我们使用同一种编程语言来编写所有内容。这使我们能够完全避免位置/上下文切换的问题,并在我们的应用程序规模不断扩大的情况下,依然能够保持高效的工作状态。
自动生成类型和架构定义语言
使用Nexus的一个显著优势在于它能够自动地生成TypeScript类型以及GraphQL架构定义语言(SDL)文件。这些自动生成的类型可以为支持GraphQL API的代码提供额外的类型安全保障。同时,生成的SDL文件也具备多种用途,例如,我们可以配置编辑器以了解API的结构,从而便于对编写的查询和变更进行自省。
Nexus免费提供类型和SDL的生成功能,只需在调用makeSchema
时提供相应配置即可启用。
import path from 'path'
const schema = makeSchema({
types: [Post, Query],
outputs: {
schema: path.join(__dirname, 'generated/schema.gen.graphql'),
typegen: path.join(__dirname, 'generated/nexusTypes.gen.ts'),
},
})
Nexus 1.0 有哪些新功能
Nexus 1.0 版有许多变化。阅读完整的更新日志并按照下面的内容进行操作,看看有什么新内容!
新包名称
Nexus 1.0 现已采用新的软件包名称。现在,所有导入操作均需来自新的路径,而非原先的 .nexus/schema
,而是改为使用 nexus
下的相应名称。
import { makeSchema } from 'nexus'
// ...
对可为 Null 性的更改
在早期的Nexus版本中,字段默认被视为不可为空,这与其他GraphQL API框架的做法有所不同,后者通常默认将字段视为可为null,除非另有指定。Nexus的开发者之所以采取这种默认策略,是因为他们认为默认允许字段不可为空可能会给API的开发带来一些长期的风险。
然而,在Nexus 1.0中,字段的默认可空性已经进行了调整,以与GraphQL的最佳实践和期望保持一致,特别是与GraphQL官方推荐的实践相符。现在,在Nexus 1.0中,如果需要将字段设为非null,则需要显式地进行声明。
const Post = objectType({
name: 'Post',
definition(t) {
t.nonNull.id('id')
t.nonNull.string('title')
t.nonNull.string('body')
},
})
此类型的 SDL 将如下所示:
type Post {
id: ID!
title: String!
body: String!
}
上述对象类型的代码包含一个名为 property
的属性,该属性为指定字段必须为非空(non-null)提供了更高的灵活性。在 API 设计面临挑战时,比如表示深度嵌套的类型结构,或者需要以编程方式构建不可为空(non-nullable)的类型,这个属性就显得尤为有用。例如,可以创建一个名为 PostNonNull
的类型。
import { queryType, stringArg, nonNull } from 'nexus'
queryType({
definition(t) {
t.field('tags', {
type: nonNull('String') // => String!
args: {
id: nonNull(stringArg()) // or nonNull('String') => String!
},
resolve() {
// ...
}
})
}
})
该函数接受一个参数,该参数可用于指定类型或参数的非空性(nonNull
)。
尽管字段现在默认允许为null,但在Nexus API中,您仍然可以选择全局或在类型级别更改这一默认行为。
queryType({
nonNullDefaults: {
output: true,
},
definition(t) {
t.string('echo', {
args: {
message: 'String',
},
resolve(_root, args) {
return args.message
},
})
},
})
在此示例中,Nexus 现在要求所有查询类型字段默认应为非空(non-null)。
如果您希望将某些字段的默认类型更改为可为空(nullable),则可以使用 nullable
函数来实现。
import { queryType, stringArg, nullable } from 'nexus'
queryType({
nonNullDefaults: {
input: true,
output: true,
},
definition(t) {
t.field('echo', {
type: nullable('String'),
args: {
message: nullable(stringArg()),
},
resolve(_root, args) {
return args.message
},
})
},
})
要深入了解Nexus如何处理可为null性,包括与API进行交互的其他相关方式,请查阅可为null性指南。
对 List API 的更改
Nexus 1.0引入了一个新功能,专门用于处理列表类型。这个功能既可以应用于输入类型,也可以应用于输出类型,其使用方式与某些其他函数类似(例如list
、nonNull
和nullable
等函数)。
import { queryType, stringArg, list } from 'nexus'
queryType({
definition(t) {
t.field('tags', {
type: list('String') // -> [String]
args: {
ids: list(stringArg()) // or list('String') -> [String]
},
resolve() {
// ...
}
})
}
})
用于创建列表的相同链接 API 依然保留,但新增的函数在处理某些不理想链接情况时提供了帮助。list
函数即为此类工具之一。
抽象类型
在 GraphQL 中,抽象类型是指 Union 和 Interface 类型。
Union 类型能够表示多态字段,其成员类型可以截然不同。
Interface 类型则允许表示多态字段,这些字段可能返回多种不同的对象类型,但它们都共享某些字段的子集。
官方 GraphQL JavaScript 包提供了三种实现抽象类型的策略,而 Nexus 1.0 现如今通过一个 API 支持了这三种策略,并在此过程中确保了类型安全。
请注意,以下示例虽以 union 类型为例,但同样适用于 interface 类型。
集中式策略
Centralized 策略允许您通过一种集中化(针对联合类型)的方式来区分联合中的不同成员类型。例如:
const SearchResult = unionType({
name: 'SearchResult',
resolveType(data) {
const __typename = data.album ? 'Song' : data.rating ? 'Movie' : data.width ? 'Photo' : null
if (!__typename) {
throw new Error(`Could not resolve the type of data passed to union type "SearchResult"`)
}
return __typename
},
definition(t) {
t.members('Photo', 'Movie', 'Song')
},
})
判别模型字段策略
判别模型字段(DMF)策略使您能够以潜在的模块化方式来区分联合类型的成员。该策略基于在由类型化为抽象类型的字段解析器返回的数据中包含的__typename
字段。以下是一个示例:
const Query = queryType({
definition(t) {
t.field('search', {
type: 'SearchResult',
args: {
pattern: stringArg(),
},
resolve(root, args, ctx) {
return ctx.db.search(args.pattern).map(result => {
const __typename = result.album ? 'Song' : result.rating ? 'Movie' : result.width ? 'Photo' : null
if (!__typename) {
throw new Error(`Could not resolve the type of data passed to union type "SearchResult"`)
}
return {
...result,
__typename,
}
})
},
})
},
})
模块化策略
模块化策略使您能够以模块化的方式区分联合类型的成员。它依赖于您实现的谓词函数,该函数使Nexus(以及背后的GraphQL.js)能够在运行时判断发送给客户端的数据是否属于特定的类型。以下是一个示例:
const Movie = objectType({
name: 'Movie',
isTypeOf(data) {
return Boolean(data.rating)
},
definition(t) {
t.string('url')
t.field('rating', {
type: 'MovieRating',
})
},
})
const Photo = objectType({
name: 'Photo',
isTypeOf(data) {
return Boolean(data.width)
},
definition(t) {
t.string('url')
t.int('width')
t.int('height')
},
})
const Song = objectType({
name: 'Song',
isTypeOf(data) {
return Boolean(data.album)
},
definition(t) {
t.string('url')
t.string('album')
},
})
阅读抽象类型指南,您将深入了解这些策略的工作原理、如何启用或禁用它们、以及它们如何确保类型安全等详细信息。
关于背衬类型的变更
在初步构建架构时,您可能会注意到一些起初并不显而易见的现象。客户端在数据图中所见的数据与内部解析器处理的数据存在差异。客户端接触的是 API 类型,而 API 设计者则处理其他内容,这些内容在 Nexus 中传统上被称为“支持”或“根”类型。在 Nexus 1.0 中,这些类型现在被全局统一称为“Source Types”。
请查阅源类型指南,以获取更多关于它们是什么、以及如何使用它们的信息。
文档和指南的改进
Nexus 1.0 带来了文档体验的提升,包括新增的指南、更优化的导航、JSDoc 支持,以及 Nexus Playground 的改进。
改进的 JSDoc 支持
JSDoc 支持改进了开发人员体验。文档内容包括:
- 清晰的解释
- 简明代码示例
- 官方文档链接
- 指向 GraphQL 规范的链接
Codesandbox 示例
Nexus的playground已经转变为一系列预先准备好的Codesandbox示例。新的Playground提供了一个预配置的Nexus API环境,您可以轻松进行fork和扩展。
结束语
Nexus 1.0是该库发展历程中的一个重要里程碑。在过去的两年多里,社区在其成长和成功中扮演了至关重要的角色。我们衷心感谢所有试用Nexus、提交问题、贡献代码并将其投入生产使用的朋友们!
请在Twitter上关注@nexusgql,并关注我们的仓库,以便获取关于Nexus未来更新的最新信息。
原文链接:https://www.prisma.io/blog/announcing-the-release-of-nexus-schema-v1-b5eno5g08d0b