
云原生 API 網關 APISIX 入門教程
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提供了一種在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 采用代碼優先方法有很多好處,包括:
在構建以架構為核心的 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/schema
,而是改為使用 nexus
下的相應名稱。
import { makeSchema } from 'nexus'
// ...
在早期的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性指南。
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 支持改進了開發人員體驗。文檔內容包括:
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