跳到主要內容

prisma-binding 到 Nexus

總覽

注意:本指南尚未完全更新,因為目前使用已棄用版本的 nexus-plugin-prisma。雖然這仍然可用,但建議使用新的 nexus-prisma 函式庫或替代的程式碼優先 GraphQL 函式庫,例如 Pothos 以繼續進行。如果您有任何問題,請加入我們的 Discord

本升級指南說明如何遷移基於 Prisma 1 並使用 prisma-binding 實作 GraphQL 伺服器的 Node.js 專案。

程式碼將遷移到 @nexus/schemanexus-plugin-prisma。與使用 prisma-bindingSDL 優先方法相反,Nexus 遵循程式碼優先方法來建構 GraphQL schema。您可以在這篇文章中了解這兩種方法的主要差異。如果您想繼續使用 SDL 優先方法,您可以按照指南prisma-binding 升級到 SDL 優先設定。

本指南也說明如何從 JavaScript 遷移到 TypeScript,因此基本上假設完全重寫您現有的應用程式。如果您想繼續在 JavaScript 中執行您的應用程式,您可以忽略與 TypeScript 設定相關的說明,並繼續像以前一樣使用 JavaScript。

本指南假設您已完成升級 Prisma ORM 層的指南。這表示您已經

  • 安裝 Prisma ORM 2.0 CLI
  • 建立您的 Prisma ORM 2.0 schema
  • 內省您的資料庫並解決潛在的schema 不相容性
  • 安裝並產生 Prisma Client

本指南進一步假設您有類似於此的檔案設定

.
├── README.md
├── package.json
├── prisma
│ └── schema.prisma
├── prisma1
│ ├── datamodel.prisma
│ └── prisma.yml
└── src
├── generated
│ └── prisma.graphql
├── index.js
└── schema.graphql

重要的部分是

  • 一個名為 prisma 的資料夾,其中包含您的 Prisma ORM 2.0 schema
  • 一個名為 src 的資料夾,其中包含您的應用程式程式碼和一個名為 schema.graphql 的 schema

如果您的專案結構不是這樣,您將需要調整指南中的說明以符合您自己的設定。

1. 安裝與配置 Nexus

1.1. 安裝 Nexus 相依性

第一步是在您的專案中安裝 Nexus 相依性

npm install @nexus/schema

接下來,安裝 Nexus 的 Prisma ORM 外掛程式,這將讓您在 GraphQL API 中公開 Prisma 模型

npm install nexus-plugin-prisma

nexus-plugin-prisma 相依性捆綁了所有必需的 Prisma ORM 相依性。因此,您應該移除在升級應用程式的 Prisma ORM 層時安裝的相依性

npm uninstall @prisma/cli @prisma/client

但請注意,您仍然可以使用熟悉的命令調用 Prisma ORM 2.0 CLI

npx prisma

1.2. 配置 TypeScript

由於您將在本指南中使用 TypeScript,因此您需要新增必要的相依性

npm install typescript ts-node-dev --save-dev

在您的專案根目錄中建立一個名為 tsconfig.json 的新檔案

touch tsconfig.json

現在將以下內容新增到新檔案中

tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true,
"strict": true,
"rootDir": "src",
"noEmit": true
},
"include": ["src/**/*"]
}

1.3. 建立您的基本 Nexus 設定

src 目錄內建立 API 的根原始碼檔案,名為 index.ts

touch src/index.ts

請注意,在本指南中,您將在 index.ts 內編寫整個應用程式。實際上,您可能希望將您的 GraphQL 類型分散在不同的檔案中,如此範例所示。

對於一些基本設定,將此程式碼新增到 index.ts

index.ts
import { queryType, makeSchema } from '@nexus/schema'
import { nexusSchemaPrisma } from 'nexus-plugin-prisma/schema'
import { GraphQLServer } from 'graphql-yoga'
import { createContext } from './context'

const Query = queryType({
definition(t) {
t.string('hello', () => {
return 'Hello Nexus!'
})
},
})

export const schema = makeSchema({
types: [Query],
plugins: [nexusSchemaPrisma({ experimentalCRUD: true })],
outputs: {
schema: __dirname + '/../schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
contextType: 'Context.Context',
sources: [
{
source: '@prisma/client',
alias: 'prisma',
},
{
source: require.resolve('./context'),
alias: 'Context',
},
],
},
})

new GraphQLServer({ schema, context: createContext() }).start(() =>
console.log(`Server ready at: https://127.0.0.1:4000`)
)

請注意,此設定已包含 Nexus 的 Prisma ORM 外掛程式的配置。這將啟用 t.modelt.crud 功能,您稍後將在本指南中了解這些功能。

typegenAutoConfig 設定中,您正在提供額外的類型,以協助您的編輯器在您開發應用程式時提供自動完成功能。現在它引用一個名為 context.ts 的檔案,您的專案中還沒有這個檔案。此檔案將包含您的 context 物件的類型,該物件會傳遞通過您的 GraphQL 解析器鏈。

src 目錄內建立新的 context.ts 檔案

touch src/context.ts

現在將以下程式碼新增到其中

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

export interface Context {
prisma: PrismaClient
}

export function createContext(): Context {
return { prisma }
}

接下來,調整您 package.json 內的 scripts 區段以包含以下命令

{
"scripts": {
"start": "node dist/server",
"clean": "rm -rf dist",
"build": "npm -s run clean && npm -s run generate && tsc",
"generate": "npm -s run generate:prisma && npm -s run generate:nexus",
"generate:prisma": "prisma generate",
"generate:nexus": "ts-node --transpile-only src/schema",
"dev": "ts-node-dev --no-notify --respawn --transpile-only src"
}
}

dev 腳本啟動一個開發伺服器,在開發應用程式時,您應該始終在背景中執行它。這很重要,因為 Nexus 在背景中執行程式碼產生。

您可以使用以下命令啟動開發伺服器

npm run dev

您應該會看到以下 CLI 輸出

Server ready at: https://127.0.0.1:4000

您的 GraphQL 伺服器現在正在 https://127.0.0.1:4000 上執行。到目前為止,它實作了一個單一 GraphQL 查詢,您可以如下所示發送它

{
hello
}

在接下來的步驟中,我們將說明如何將您現有的 SDL 優先 GraphQL schema(使用 prisma-binding 實作)遷移到使用 Nexus 的等效設定。

2. 建立您的 GraphQL 類型

升級過程的下一步是建立您的GraphQL 類型。在這種情況下,您的 GraphQL 類型將反映 Prisma 模型(就像您的 prisma-binding 設定中可能的情況一樣)。如果 GraphQL 類型偏離 Prisma 模型,您將可以使用 Nexus API 輕鬆調整公開的 GraphQL 類型。

為了本指南的目的,您將將所有程式碼保留在單一檔案中。但是,您可以根據您的個人偏好來組織檔案並相應地 import

在 Nexus 中,GraphQL 類型是透過 objectType 函式定義的。匯入 objectType,然後從您的第一個 GraphQL 類型的骨架開始。在本例中,我們從將 Prisma schema 的 User 模型對應到 GraphQL 開始

import { objectType } from 'nexus'

const User = objectType({
name: 'User',
definition(t) {
// the fields of the type will be defined here
},
})

有了這段程式碼,您就可以開始逐一公開 User 模型的欄位。您可以使用編輯器的自動完成功能來節省一些輸入。在 definition 函式的本體內,輸入 t.model.,然後按下 CTRL+SPACE。這將帶出自動完成功能,並建議 User 模型上定義的所有欄位

Exposing Prisma model fields with t.model

請注意,t 上的 model 屬性是由 nexus-plugin-prisma 提供的。它利用來自您的 Prisma schema 的類型資訊,讓您透過 GraphQL 公開您的 Prisma 模型。

以這種方式,您可以開始完成您的物件類型定義,直到您公開模型的所有欄位

const User = objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.name()
t.model.jsonData()
t.model.role()
t.model.profile()
t.model.posts()
},
})

此時,任何關聯欄位都可能給您 TypeScript 錯誤(在本例中,那是 profileposts,它們都指向其他物件類型)。這是預期的,在您新增剩餘的類型後,這些錯誤將自動解決。

注意:請務必讓您使用 npm run dev 啟動的 Nexus 開發伺服器始終執行。當您儲存檔案時,它會不斷更新產生的 Nexus 類型,從而在背景中啟用自動完成功能。

請注意,t.model.posts 關聯公開了 Post 物件的列表。預設情況下,Nexus 僅為該列表公開分頁屬性 – 如果您也想為該關聯新增排序篩選,您需要明確啟用這些

const User = objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.name()
t.model.jsonData()
t.model.role()
t.model.profile()
t.model.posts({
filtering: true,
ordering: true,
})
},
})

在使用 objectType 函式定義類型後,您還需要手動將其新增到您正在使用 Nexus 建構的 GraphQL schema。您可以透過將其新增到 types 來完成,這些類型作為 makeSchema 函式的選項提供

export const schema = makeSchema({
types: [Query, User],
plugins: [nexusSchemaPrisma()],
outputs: {
schema: __dirname + '/../schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: '@prisma/client',
alias: 'prisma',
},
],
},
})

完成第一個類型後,您可以開始定義剩餘的類型。

展開以檢視範例資料模型的完整版本

若要使用 Nexus 公開所有範例 Prisma 模型,需要以下程式碼

const User = objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.name()
t.model.jsonData()
t.model.role()
t.model.profile()
t.model.posts({
filtering: true,
ordering: true,
})
},
})

const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.createdAt()
t.model.updatedAt()
t.model.title()
t.model.content()
t.model.published()
t.model.author()
t.model.authorId()
t.model.categories({
filtering: true,
ordering: true,
})
},
})

const Profile = objectType({
name: 'Profile',
definition(t) {
t.model.id()
t.model.bio()
t.model.userId()
t.model.user()
},
})

const Category = objectType({
name: 'Category',
definition(t) {
t.model.id()
t.model.name()
t.model.posts({
filtering: true,
ordering: true,
})
},
})

請務必將所有新定義的類型包含在提供給 makeSchematypes 選項中

export const schema = makeSchema({
types: [Query, User, Post, Profile, Category],
plugins: [nexusSchemaPrisma()],
outputs: {
schema: __dirname + '/../schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: '@prisma/client',
alias: 'prisma',
},
],
},
})

您可以在 ./schema.graphql 中產生的 GraphQL schema 檔案中檢視 SDL 中 GraphQL schema 的目前版本。

3. 遷移 GraphQL 運算

下一步,您可以開始將所有 GraphQL 查詢變更從「先前」的 GraphQL API 遷移到使用 Nexus 建構的新 API。

在本指南中,將使用以下範例 GraphQL schema

# import Post from './generated/prisma.graphql'
# import User from './generated/prisma.graphql'
# import Category from './generated/prisma.graphql'

input UserUniqueInput {
id: String
email: String
}

type Query {
posts(searchString: String): [Post!]!
user(userUniqueInput: UserUniqueInput!): User
users(where: UserWhereInput, orderBy: Enumerable<UserOrderByInput>, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
}

type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: ID!): Post
updateBio(userUniqueInput: UserUniqueInput!, bio: String!): User
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
}

3.1. 遷移 GraphQL 查詢

在本節中,您將從 prisma-binding 遷移所有 GraphQL 查詢到 Nexus。

3.1.1. 遷移 users 查詢(使用 forwardTo

在我們的範例 API 中,範例 GraphQL schema 中的 users 查詢定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Query {
users(where: UserWhereInput, orderBy: Enumerable<UserOrderByInput>, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
# ... other queries
}
使用 prisma-binding 的解析器實作
const resolvers = {
Query: {
users: forwardTo('prisma'),
// ... other resolvers
},
}

若要使用 Nexus 鏡像相同的行為,您可以使用 definition 函式內 t 變數上的 crud 屬性。

model 類似,此屬性可用,因為您正在使用 nexus-prisma-plugin,它利用來自您的 Prisma 模型的類型資訊,並在底層自動產生解析器。crud 屬性也支援自動完成功能,因此您可以再次在編輯器中探索所有可用的查詢

Using t.crud to generate resolvers

使用 nexus-prisma-plugin 轉發查詢

若要將 users 查詢新增到您的 GraphQL API,請將以下幾行新增到查詢類型定義

const Query = queryType({
definition(t) {
t.crud.users({
filtering: true,
ordering: true,
})
},
})

如果您正在執行 Nexus 開發伺服器,您可以儲存檔案,您的 GraphQL API 將會更新以公開新的 users 查詢。您也可以透過查看產生的 schema.graphql 檔案內的 Query 類型來觀察到這一點

type Query {
users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable<UserOrderByInput>, skip: Int, where: UserWhereInput): [User!]!
}

您現在可以針對新的 API 編寫您的第一個查詢,例如

{
users {
id
name
profile {
id
bio
}
posts {
id
title
categories {
id
name
}
}
}
}

如果您的應用程式使用 forwardTo 公開來自 Prisma ORM 的所有 CRUD 運算,您現在可以繼續使用透過 t.crud 的相同方法新增所有剩餘的運算。若要了解如何使用 Nexus 定義和解析「自訂」查詢,請繼續閱讀下一節。

3.1.2. 遷移 posts(searchString: String): [Post!]! 查詢

posts 查詢定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Query {
posts(searchString: String): [Post!]!
# ... other queries
}
使用 prisma-binding 的解析器實作
const resolvers = {
Query: {
posts: (_, args, context, info) => {
return context.prisma.query.posts(
{
where: {
OR: [
{ title_contains: args.searchString },
{ content_contains: args.searchString },
],
},
},
info
)
},
// ... other resolvers
},
}
使用 nexus 的程式碼優先 schema 定義

若要使用 Nexus 獲得相同的行為,您需要將 t.field 定義新增到 queryType

const Query = queryType({
definition(t) {
// ... previous queries

t.list.field('posts', {
type: 'Post',
nullable: false,
args: { searchString: stringArg() },
})
},
})

雖然這段程式碼可能會在您的編輯器中給您一個類型錯誤,但您已經可以查看 schema.graphql 內 GraphQL schema 的產生 SDL 版本。您會注意到這已經將正確的定義新增到您的 GraphQL schema

type Query {
posts(searchString: String): [Post!]!
users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable<UserOrderByInput>, skip: Int, where: UserWhereInput): [User!]!
}

但是,程式碼缺少實際的解析器邏輯。這就是您接下來要新增的內容。

使用 nexus 的解析器實作

您可以使用 Nexus 如下所示新增解析器

const Query = queryType({
definition(t) {
// ... previous queries

t.list.field('posts', {
type: 'Post',
nullable: false,
args: { searchString: stringArg() },
resolve: (_, args, context) => {
return context.prisma.post.findMany({
where: {
OR: [
{
title: { contains: args.searchString },
},
{
content: { contains: args.searchString },
},
],
},
})
},
})
},
})

若要驗證實作,您現在可以發送以下範例查詢到您的 GraphQL 伺服器,例如

{
posts {
id
title
author {
id
name
}
}
}

3.1.2. 遷移 user(uniqueInput: UserUniqueInput): User 查詢

在我們的範例應用程式中,user 查詢定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Query {
user(userUniqueInput: UserUniqueInput): User
# ... other queries
}

input UserUniqueInput {
id: String
email: String
}

請注意,這是一個有點牽強的範例,用於示範 Nexus 中 input 類型的用法。

使用 prisma-binding 的解析器實作
const resolvers = {
Query: {
user: (_, args, context, info) => {
return context.prisma.query.user(
{
where: args.userUniqueInput,
},
info
)
},
// ... other resolvers
},
}
使用 nexus 的程式碼優先 schema 定義

若要使用 Nexus 獲得相同的行為,您需要將 t.field 定義新增到 queryType,並定義一個 inputObjectType,其中包含 User 模型的兩個 @unique 欄位

import { inputObjectType, arg } from '@nexus/schema'

const UserUniqueInput = inputObjectType({
name: 'UserUniqueInput',
definition(t) {
t.string('id')
t.string('email')
},
})

const Query = queryType({
definition(t) {
// ... previous queries

t.field('user', {
type: 'User',
args: {
userUniqueInput: arg({
type: 'UserUniqueInput',
nullable: false,
}),
},
})
},
})

由於 UserUniqueInput 是 GraphQL schema 中的新類型,因此您需要再次將其新增到傳遞給 makeSchematypes 選項

export const schema = makeSchema({
types: [Query, User, Post, Profile, Category, UserUniqueInput],
plugins: [nexusSchemaPrisma()],
outputs: {
schema: __dirname + '/../schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: '@prisma/client',
alias: 'prisma',
},
],
},
})

如果您查看 schema.graphql 內 GraphQL schema 的產生 SDL 版本,您會注意到此變更已將正確的定義新增到您的 GraphQL schema

type Query {
posts(searchString: String): [Post!]
user(userUniqueInput: UserUniqueInput!): User
users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable<UserOrderByInput>, skip: Int, where: UserWhereInput): [User!]!
}

input UserUniqueInput {
email: String
id: String
}

您甚至可以透過 GraphQL Playground 發送各自的查詢

{
user(userUniqueInput: { email: "alice@prisma.io" }) {
id
name
}
}

但是,由於解析器尚未實作,因此您還不會獲得任何資料。

使用 nexus 的程式碼優先解析器實作

那是因為您仍然缺少該查詢的解析器實作。您可以使用 Nexus 如下所示新增解析器

const UserUniqueInput = inputObjectType({
name: 'UserUniqueInput',
definition(t) {
t.string('id')
t.string('email')
},
})

const Query = queryType({
definition(t) {
// ... previous queries

t.field('user', {
type: 'User',
nullable: true,
args: {
userUniqueInput: arg({
type: 'UserUniqueInput',
nullable: false,
}),
},
resolve: (_, args, context) => {
return context.prisma.user.findUnique({
where: {
id: args.userUniqueInput?.id,
email: args.userUniqueInput?.email,
},
})
},
})
},
})

如果您重新發送與之前相同的查詢,您會發現它現在傳回實際資料。

3.2. 遷移 GraphQL 變更

在本節中,您將範例 schema 中的 GraphQL 變更遷移到 Nexus。

3.2.1. 定義 Mutation 類型

遷移任何變更的第一步是定義您的 GraphQL API 的 Mutation 類型。完成後,您可以逐步將運算新增到其中。將以下定義新增到 index.ts

import { mutationType } from '@nexus/schema'

const Mutation = mutationType({
definition(t) {
// your GraphQL mutations + resolvers will be defined here
},
})

為了確保新的 Mutation 類型被 Nexus 擷取,您需要將其新增到提供給 makeSchematypes

export const schema = makeSchema({
types: [Query, User, Post, Profile, Category, UserUniqueInput, Mutation],
plugins: [nexusSchemaPrisma()],
outputs: {
schema: __dirname + '/../schema.graphql',
typegen: __dirname + '/generated/nexus.ts',
},
typegenAutoConfig: {
sources: [
{
source: '@prisma/client',
alias: 'prisma',
},
],
},
})

3.2.2. 遷移 createUser 變更(使用 forwardTo

在範例應用程式中,範例 GraphQL schema 中的 createUser 變更定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Mutation {
createUser(data: UserCreateInput!): User!
# ... other mutations
}
使用 prisma-binding 的解析器實作
const resolvers = {
Mutation: {
createUser: forwardTo('prisma'),
// ... other resolvers
},
}

與轉發 GraphQL 查詢類似,您可以使用 definition 函式內 t 變數上的 crud 屬性,以便公開 Prisma 模型的完整 CRUD 功能。

model 類似,此屬性可用,因為您正在使用 nexus-prisma-plugin,它利用來自您的 Prisma 模型的類型資訊,並在底層自動產生解析器。crud 屬性在定義變更時也支援自動完成功能,因此您可以再次在編輯器中探索所有可用的運算

Generating resolvers with t.crud

使用 nexus-prisma-plugin 轉發變更

若要將 createUser 變更新增到您的 GraphQL API,請將以下幾行新增到查詢類型定義

const Mutation = mutationType({
definition(t) {
t.crud.createOneUser({
alias: 'createUser',
})
},
})

請注意,GraphQL schema 中變更的預設名稱是 createOneUser(以 t.crud 公開的函式命名)。為了將其重新命名為 createUser,您需要提供 alias 屬性。

如果您正在執行 Nexus 開發伺服器,您可以儲存檔案,您的 GraphQL API 將會更新以公開新的 createUser 變更。您也可以透過查看產生的 schema.graphql 檔案內的 Mutation 類型來觀察到這一點

type Mutation {
createUser(data: UserCreateInput!): User!
}

您現在可以針對新的 API 編寫您的第一個變更,例如

mutation {
createUser(data: { name: "Alice", email: "alice@prisma.io" }) {
id
}
}

如果您的應用程式使用 forwardTo 公開來自 Prisma Client 的所有 CRUD 運算,您現在可以繼續使用透過 t.crud 的相同方法新增所有剩餘的運算。若要了解如何使用 Nexus 定義和解析「自訂」變更,請繼續閱讀下一節。

3.2.3. 遷移 createDraft(title: String!, content: String, authorId: String!): Post! 查詢

在範例應用程式中,createDraft 變更定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Mutation {
createDraft(title: String!, content: String, authorId: String!): Post!
# ... other mutations
}
使用 prisma-binding 的解析器實作
const resolvers = {
Mutation: {
createDraft: (_, args, context, info) => {
return context.prisma.mutation.createPost(
{
data: {
title: args.title,
content: args.content,
author: {
connect: {
id: args.authorId,
},
},
},
},
info
)
},
// ... other resolvers
},
}
使用 nexus 的程式碼優先 schema 定義

若要使用 Nexus 獲得相同的行為,您需要將 t.field 定義新增到 mutationType

const Mutation = mutationType({
definition(t) {
// ... previous mutations

t.field('createDraft', {
type: 'Post',
args: {
title: stringArg({ nullable: false }),
content: stringArg(),
authorId: stringArg({ nullable: false }),
},
})
},
})

如果您查看 schema.graphql 內 GraphQL schema 的產生 SDL 版本,您會注意到這已經將正確的定義新增到您的 GraphQL schema

type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: String!): Post!
}

您甚至可以透過 GraphQL Playground 發送各自的變更

mutation {
createDraft(title: "Hello World", authorId: "__AUTHOR_ID__") {
id
published
author {
id
name
}
}
}

但是,由於解析器尚未實作,因此不會建立新的 Post 記錄,並且您不會在回應中獲得任何資料。

使用 nexus 的解析器實作

那是因為您仍然缺少該變更的解析器實作。您可以使用 Nexus 如下所示新增解析器

const Mutation = mutationType({
definition(t) {
// ... previous mutations

t.field('createDraft', {
type: 'Post',
args: {
title: stringArg({ nullable: false }),
content: stringArg(),
authorId: stringArg({ nullable: false }),
},
resolve: (_, args, context) => {
return context.prisma.post.create({
data: {
title: args.title,
content: args.content,
author: {
connect: { id: args.authorId },
},
},
})
},
})
},
})

如果您重新發送與之前相同的查詢,您會發現它現在建立了一個新的 Post 記錄並傳回有效資料。

3.2.4. 遷移 updateBio(bio: String, userUniqueInput: UserUniqueInput!): User 變更

在範例應用程式中,updateBio 變更定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Mutation {
updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User
# ... other mutations
}
使用 prisma-binding 的解析器實作
const resolvers = {
Mutation: {
updateBio: (_, args, context, info) => {
return context.prisma.mutation.updateUser(
{
data: {
profile: {
update: { bio: args.bio },
},
},
where: { id: args.userId },
},
info
)
},
// ... other resolvers
},
}
使用 nexus 的程式碼優先 schema 定義

若要使用 Nexus 獲得相同的行為,您需要將 t.field 定義新增到 mutationType

const Mutation = mutationType({
definition(t) {
// ... previous mutations

t.field('updateBio', {
type: 'User',
args: {
userUniqueInput: arg({
type: 'UserUniqueInput',
nullable: false,
}),
bio: stringArg({ nullable: false }),
},
})
},
})

如果您查看 schema.graphql 內 GraphQL schema 的產生 SDL 版本,您會注意到這已經將正確的定義新增到您的 GraphQL schema

type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: String!): Post!
updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User
}

您甚至可以透過 GraphQL Playground 發送各自的變更

mutation {
updateBio(
userUniqueInput: { email: "alice@prisma.io" }
bio: "I like turtles"
) {
id
name
profile {
id
bio
}
}
}

但是,由於解析器尚未實作,因此資料庫中不會更新任何內容,並且您不會在回應中獲得任何資料。

使用 nexus 的解析器實作

那是因為您仍然缺少該查詢的解析器實作。您可以使用 Nexus 如下所示新增解析器

const Mutation = mutationType({
definition(t) {
// ... previous mutations

t.field('updateBio', {
type: 'User',
args: {
userUniqueInput: arg({
type: 'UserUniqueInput',
nullable: false
}),
bio: stringArg()
},
resolve: (_, args, context) => {
return context.prisma.user.update({
where: {
id: args.userUniqueInput?.id,
email: args.userUniqueInput?.email
},
data: {
profile: {
create: { bio: args.bio }
}
}
})
}
}
}
})

如果您重新發送與之前相同的查詢,您會發現它現在傳回實際資料而不是 null

3.2.5. 遷移 addPostToCategories(postId: String!, categoryIds: [String!]!): Post 變更

在我們的範例應用程式中,addPostToCategories 變更定義和實作如下。

使用 prisma-binding 的 SDL schema 定義
type Mutation {
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
# ... other mutations
}
使用 prisma-binding 的解析器實作
const resolvers = {
Mutation: {
addPostToCategories: (_, args, context, info) => {
const ids = args.categoryIds.map((id) => ({ id }))
return context.prisma.mutation.updatePost(
{
data: {
categories: {
connect: ids,
},
},
where: {
id: args.postId,
},
},
info
)
},
// ... other resolvers
},
}
使用 nexus 的程式碼優先 schema 定義

若要使用 Nexus 獲得相同的行為,您需要將 t.field 定義新增到 mutationType

const Mutation = mutationType({
definition(t) {
// ... mutations from before

t.field('addPostToCategories', {
type: 'Post',
args: {
postId: stringArg({ nullable: false }),
categoryIds: stringArg({
list: true,
nullable: false,
}),
},
})
},
})

如果您查看 schema.graphql 內 GraphQL schema 的產生 SDL 版本,您會注意到這已經將正確的定義新增到您的 GraphQL schema

type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: String!): Post!
updateBio(bio: String, userUniqueInput: UserUniqueInput!): User
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
}

您甚至可以透過 GraphQL Playground 發送各自的查詢

mutation {
addPostToCategories(
postId: "__AUTHOR_ID__"
categoryIds: ["__CATEGORY_ID_1__", "__CATEGORY_ID_2__"]
) {
id
title
categories {
id
name
}
}
}

但是,由於解析器尚未實作,因此資料庫中不會更新任何內容,並且您不會在回應中獲得任何資料。

使用 nexus 的解析器實作

那是因為您仍然缺少該查詢的解析器實作。您可以使用 Nexus 如下所示新增解析器

const Mutation = mutationType({
definition(t) {
// ... mutations from before
t.field('addPostToCategories', {
type: 'Post',
args: {
postId: stringArg({ nullable: false }),
categoryIds: stringArg({
list: true,
nullable: false,
}),
},
resolve: (_, args, context) => {
const ids = args.categoryIds.map((id) => ({ id }))
return context.prisma.post.update({
where: {
id: args.postId,
},
data: {
categories: { connect: ids },
},
})
},
})
},
})

如果您重新發送與之前相同的查詢,您會發現它現在傳回實際資料而不是 null

4. 清理

由於整個應用程式現在已升級到 Prisma ORM 2.0 和 Nexus,您可以刪除所有不必要的檔案並移除不再需要的相依性。

4.1. 清理 npm 相依性

您可以先移除與 Prisma 1 設定相關的 npm 相依性

npm uninstall graphql-cli prisma-binding prisma1

4.2. 刪除未使用的檔案

接下來,刪除您的 Prisma 1 設定檔

rm prisma1/datamodel.prisma prisma1/prisma.yml

您也可以刪除任何剩餘的 .js 檔案、舊的 schema.graphqlprisma.graphql 檔案。

4.3. 停止 Prisma ORM 伺服器

最後,您可以停止執行您的 Prisma ORM 伺服器。