跳到主要內容

舊 Nexus 到 新 Nexus

概覽

資訊

本指南並非完全最新,因為它目前使用的是已棄用的版本的nexus-plugin-prisma。雖然它仍然可用,但建議今後使用新的nexus-prisma庫或替代的 Code-first GraphQL 庫,例如Pothos。如果您有任何疑問,請隨時在我們的Discord上分享。

本升級指南描述瞭如何升級一個基於Prisma 1,並使用nexus(< v0.12.0)或@nexus/schema以及nexus-prisma(< v4.0.0)來實現 GraphQL 伺服器的專案。

程式碼將升級到最新版本的 @nexus/schema。此外,nexus-prisma 包將被新的nexus-plugin-prisma取代。

本指南假定你已完成了Prisma ORM 層升級指南。這意味著你已經

  • 安裝了 Prisma ORM 2 CLI
  • 建立了你的 Prisma ORM 2 schema
  • 內省了你的資料庫並解決了潛在的schema 不相容性
  • 安裝並生成了 Prisma Client

本指南還假設你的檔案設定類似於此

.
├── README.md
├── package.json
├── prisma
│ └── schema.prisma
├── prisma1
│ ├── datamodel.prisma
│ └── prisma.yml
└── src
├── generated
│ ├── nexus-prisma
│ ├── nexus.ts
│ ├── prisma-client
│ └── schema.graphql
├── types.ts
└── index.ts

重要的部分是

  • 一個名為 prisma 的資料夾,其中包含你的 Prisma ORM 2 schema
  • 一個名為 src 的資料夾,其中包含你的應用程式程式碼

如果你的專案結構不是這樣,你需要根據自己的設定調整指南中的說明。

1. 升級 Nexus 依賴

首先,你可以刪除舊的 Nexus 和 Prisma 1 依賴

npm uninstall nexus nexus-prisma prisma-client-lib prisma1

然後,你可以在你的專案中安裝最新的 @nexus/schema 依賴

npm install @nexus/schema

接下來,安裝 Nexus 的 Prisma ORM 外掛,它將允許你在 GraphQL API 中公開 Prisma ORM 模型(這是以前 nexus-prisma 包的新等效項)

npm install nexus-plugin-prisma

nexus-plugin-prisma 依賴項捆綁了所有必需的 Prisma ORM 依賴項。因此,你應該刪除你在升級應用程式的 Prisma ORM 層時新增安裝的依賴項

npm uninstall @prisma/cli @prisma/client

但請注意,你仍然可以使用熟悉的命令來呼叫 Prisma ORM 2 CLI

npx prisma -v

注意:如果你在執行 npx prisma -v 時看到 Prisma 1 CLI 的輸出,請務必刪除你的 node_modules 資料夾並重新執行 npm install

2. 更新 Nexus 和 Prisma ORM 的配置

首先,你可以刪除在新設定中不再需要的舊匯入

import { makePrismaSchema, prismaObjectType } from 'nexus-prisma'
import datamodelInfo from './generated/nexus-prisma'
import { prisma } from './generated/prisma-client'

相反,你現在將以下內容匯入到你的應用程式中

import { nexusSchemaPrisma } from 'nexus-plugin-prisma/schema'
import { objectType, makeSchema, queryType, mutationType } from '@nexus/schema'
import { PrismaClient } from '@prisma/client'

接下來你需要調整當前建立 GraphQLSchema 的程式碼,這很可能目前是透過程式碼中的 makePrismaSchema 函式完成的。由於此函式是從已刪除的 nexus-prisma 包中匯入的,因此你需要將其替換為 @nexus/schema 包中的 makeSchema 函式。Nexus 的 Prisma ORM 外掛在最新版本中的使用方式也發生了變化。

以下是此類配置的示例

./src/index.ts
 const schema = makePrismaSchema({
const schema = makeSchema({

// Provide all the GraphQL types we've implemented
types: [Query, Mutation, UserUniqueInput, User, Post, Category, Profile],

// Configure the interface to Prisma
prisma: {
datamodelInfo,
client: prisma,
},
plugins: [nexusSchemaPrisma({
experimentalCRUD: true,
})],

// Specify where Nexus should put the generated files
outputs: {
schema: path.join(__dirname, './generated/schema.graphql'),
typegen: path.join(__dirname, './generated/nexus.ts'),
},

// Configure nullability of input arguments: All arguments are non-nullable by default
nonNullDefaults: {
input: false,
output: false,
},

// Configure automatic type resolution for the TS representations of the associated types
typegenAutoConfig: {
sources: [
{
source: path.join(__dirname, './types.ts'),
alias: 'types',
},
],
contextType: 'types.Context',
},
})

如果你之前對透過解析器鏈傳遞的 GraphQL context 物件進行了型別定義,則需要像這樣調整型別

./src/types.ts
import { Prisma } from './generated/prisma-client'
import { PrismaClient } from '@prisma/client'

export interface Context {
prisma: Prisma
prisma: PrismaClient
}

3. 遷移你的 GraphQL 型別

以下是使用最新版本 @nexus/schemanexus-plugin-prisma 建立 GraphQL 型別的兩種方法之間主要區別的快速概覽。

  • prismaObjectType 函式不再可用,所有型別都使用 Nexus 的 objectType 函式建立。
  • 要透過 Nexus 公開 Prisma 模型,你可以使用 t.model 屬性,該屬性已新增到傳遞給 Nexus 的 definition 函式的 t 引數中。t.model 讓你能夠訪問 Prisma 模型的屬性並允許你公開它們。
  • 透過 Nexus 公開 Prisma 模型的 CRUD 操作遵循類似的方法。這些透過你的 queryTypemutationType 型別的 definition 函式中的 t.crud 公開。

3.1. 遷移 Post 型別

使用以前的 nexus-prisma 包進行型別定義

在示例應用程式中,User 型別定義如下

const User = prismaObjectType({
name: 'User',
definition(t) {
t.prismaFields([
'id',
'name',
'email',
'jsonData',
'role'
{
name: 'posts',
args: [], // remove the arguments from the `posts` field of the `User` type in the Prisma schema
},
])
},
})

使用最新版本 @nexus/schemanexus-plugin-prisma 進行型別定義

使用最新版本 @nexus/schema,你現在可以在主 schema 例項上訪問 objectType 函式,並像這樣公開 Prisma 模型中的所有欄位

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

請注意,t.model 會檢視作為引數傳遞給 objectType 函式的物件中的 name 屬性,並將其與你的 Prisma schema 中的模型進行匹配。在本例中,它與 User 模型匹配。因此,t.model 會公開以 User 模型欄位命名的函式。

此時,你可能會在關聯欄位 postsprofile 上看到錯誤,例如

//delete-next-line
Missing type Post, did you forget to import a type to the root query?

這是因為你尚未將 PostProfile 型別新增到 GraphQL schema 中,一旦這些型別也成為 GraphQL schema 的一部分,錯誤就會消失!

3.2. 遷移 Post 型別

使用以前的 nexus-prisma 包進行型別定義

在示例應用程式中,Post 型別定義如下

const Post = prismaObjectType({
name: 'Post',
definition(t) {
t.prismaFields(['*'])
},
})

prismaFields 中的星號表示公開所有 Prisma 欄位。

使用最新版本 @nexus/schemanexus-plugin-prisma 進行型別定義

使用最新版本 @nexus/schema,你需要顯式公開所有欄位,沒有選項可以公開 Prisma 模型中的所有內容。

因此,Post 的新定義必須顯式列出其所有欄位

const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.content()
t.model.published()
t.model.author()
t.model.categories()
},
})

請注意,t.model 會檢視 name 屬性,並將其與你的 Prisma schema 中的模型進行匹配。在本例中,它與 Post 模型匹配。因此,t.model 會公開以 Post 模型欄位命名的函式。

3.3. 遷移 Profile 型別

使用以前的 nexus-prisma 包進行型別定義

在示例應用程式中,Profile 型別定義如下

const Profile = prismaObjectType({
name: 'Profile',
definition(t) {
t.prismaFields(['*'])
},
})

prismaFields 中的星號表示公開所有 Prisma 欄位。

使用最新版本 @nexus/schemanexus-plugin-prisma 進行型別定義

使用最新版本 @nexus/schema,你需要顯式公開所有欄位,沒有選項可以公開 Prisma 模型中的所有內容。

因此,Profile 的新定義必須顯式列出其所有欄位

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

請注意,t.model 會檢視 name 屬性,並將其與你的 Prisma schema 中的模型進行匹配。在本例中,它與 Profile 模型匹配。因此,t.model 會公開以 Profile 模型欄位命名的函式。

3.4. 遷移 Category 型別

使用以前的 nexus-prisma 包進行型別定義

在示例應用程式中,Category 型別定義如下

const Category = prismaObjectType({
name: 'Category',
definition(t) {
t.prismaFields(['*'])
},
})

prismaFields 中的星號表示公開所有 Prisma ORM 欄位。

使用最新版本 @nexus/schemanexus-plugin-prisma 進行型別定義

使用最新版本 @nexus/schema,你需要顯式公開所有欄位,沒有選項可以公開 Prisma 模型中的所有內容。

因此,Category 的新定義必須顯式列出其所有欄位

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

請注意,t.model 會檢視 name 屬性,並將其與你的 Prisma schema 中的模型進行匹配。在本例中,它與 Category 模型匹配。因此,t.model 會公開以 Category 模型欄位命名的函式。

4. 遷移 GraphQL 操作

下一步,你可以開始將所有 GraphQL 查詢突變從“以前的” GraphQL API 遷移到新的 API。

對於本指南,將使用以下 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
}

4.1. 遷移 GraphQL 查詢

在本節中,你將把所有 GraphQL 查詢從舊版本 nexusnexus-prisma 遷移到最新版本 @nexus/schemanexus-plugin-prisma

4.1.1. 遷移 users 查詢

在我們的示例 API 中,示例 GraphQL schema 中的 users 查詢實現如下。

const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.prismaFields(['users'])
},
})

為了在新 Nexus 中獲得相同的行為,你需要在 t.crud 上呼叫 users 函式

schema.queryType({
definition(t) {
t.crud.users({
filtering: true,
ordering: true,
pagination: true,
})
},
})

回想一下,crud 屬性是由 nexus-plugin-prisma 新增到 t 的(使用與 t.model 相同的機制)。

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

在示例 API 中,posts 查詢實現如下

queryType({
definition(t) {
t.list.field('posts', {
type: 'Post',
args: {
searchString: stringArg({ nullable: true }),
},
resolve: (parent, { searchString }, context) => {
return context.prisma.posts({
where: {
OR: [
{ title_contains: searchString },
{ content_contains: searchString },
],
},
})
},
})
},
})

此查詢唯一需要更新的是對 Prisma ORM 的呼叫,因為新的 Prisma Client API 與 Prisma 1 中使用的有所不同。

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

請注意,db 物件由 nexus-plugin-prisma 自動附加到 context。它代表你的 PrismaClient 例項,使你能夠在解析器中向資料庫傳送查詢。

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

在示例 API 中,user 查詢實現如下

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

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

你現在需要調整對 prisma 例項的呼叫,因為新的 Prisma Client API 與 Prisma 1 中使用的有所不同。

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

4.2. 遷移 GraphQL 突變

在本節中,你將把 GraphQL 突變從示例 schema 遷移到最新版本 @nexus/schemanexus-plugin-prisma

4.2.1. 遷移 createUser 突變

在我們的示例 API 中,示例 GraphQL schema 中的 createUser 突變實現如下。

const Mutation = prismaObjectType({
name: 'Mutation',
definition(t) {
t.prismaFields(['createUser'])
},
})

為了在最新版本 @nexus/schemanexus-plugin-prisma 中獲得相同的行為,你需要在 t.crud 上呼叫 createOneUser 函式並傳遞一個 alias,以便將 GraphQL schema 中的欄位重新命名為 createUser(否則它將被命名為 createOneUser,即所用函式的名稱)

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

回想一下,crud 屬性是由 nexus-plugin-prisma 新增到 t 的(使用與 t.model 相同的機制)。

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

在示例應用程式中,createDraft 突變實現如下。

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

你現在需要調整對 prisma 例項的呼叫,因為新的 Prisma Client API 與 Prisma 1 中使用的有所不同。

const Mutation = mutationType({
definition(t) {
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 },
},
},
})
},
})
},
})

4.2.3. 遷移 updateBio(bio: String, userUniqueInput: UserUniqueInput!): User 突變

在示例 API 中,updateBio 突變定義和實現如下。

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

你現在需要調整對 prisma 例項的呼叫,因為新的 Prisma Client API 與 Prisma 1 中使用的有所不同。

const Mutation = mutationType({
definition(t) {
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 },
},
},
})
},
})
},
})

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

在示例 API 中,addPostToCategories 突變定義和實現如下。

mutationType({
definition(t) {
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.updatePost({
where: {
id: args.postId,
},
data: {
categories: { connect: ids },
},
})
},
})
},
})

你現在需要調整對 prisma 例項的呼叫,因為新的 Prisma Client API 與 Prisma 1 中使用的有所不同。

const Mutation = mutationType({
definition(t) {
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 },
},
})
},
})
},
})

5. 清理

5.1. 清理 npm 依賴

如果你還沒有,現在可以解除安裝與 Prisma 1 設定相關的依賴項

npm uninstall prisma1 prisma-client-lib

5.2. 刪除未使用的檔案

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

rm -rf src/generated
rm -rf prisma1

5.3. 停止 Prisma ORM 伺服器

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

© . This site is unofficial and not affiliated with Prisma Data, Inc.