prisma-binding 到 Nexus
總覽
注意:本指南尚未完全更新,因為目前使用已棄用版本的
nexus-plugin-prisma
。雖然這仍然可用,但建議使用新的nexus-prisma
函式庫或替代的程式碼優先 GraphQL 函式庫,例如 Pothos 以繼續進行。如果您有任何問題,請加入我們的 Discord。
本升級指南說明如何遷移基於 Prisma 1 並使用 prisma-binding
實作 GraphQL 伺服器的 Node.js 專案。
程式碼將遷移到 @nexus/schema
和 nexus-plugin-prisma
。與使用 prisma-binding
的SDL 優先方法相反,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
現在將以下內容新增到新檔案中
{
"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
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.model
和 t.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
模型上定義的所有欄位
請注意,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 錯誤(在本例中,那是 profile
和 posts
,它們都指向其他物件類型)。這是預期的,在您新增剩餘的類型後,這些錯誤將自動解決。
注意:請務必讓您使用
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,
})
},
})
請務必將所有新定義的類型包含在提供給 makeSchema
的 types
選項中
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
屬性也支援自動完成功能,因此您可以再次在編輯器中探索所有可用的查詢
使用 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 中的新類型,因此您需要再次將其新增到傳遞給 makeSchema
的 types
選項
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 擷取,您需要將其新增到提供給 makeSchema
的 types
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
屬性在定義變更時也支援自動完成功能,因此您可以再次在編輯器中探索所有可用的運算
使用 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.graphql
和 prisma.graphql
檔案。
4.3. 停止 Prisma ORM 伺服器
最後,您可以停止執行您的 Prisma ORM 伺服器。