跳至主要内容

prisma-binding 到 Nexus

概述

注意:本指南尚未完全更新,因为它目前使用的是 已弃用 版本的 nexus-plugin-prisma。虽然这仍然有效,但建议使用新的 nexus-prisma 库或其他代码优先 GraphQL 库(如 Pothos)向前发展。如果你有任何问题,请加入我们的 Discord

本升级指南介绍如何迁移基于 Prisma 1 且使用 prisma-binding 实现 GraphQL 服务器的 Node.js 项目。

代码将迁移到 @nexus/schemanexus-plugin-prisma。与使用 prisma-bindingSDL-first 方法相反,Nexus 遵循代码优先方法来构建 GraphQL 架构。你可以在 这篇文章 中了解这两种方法的主要区别。如果你想继续使用 SDL-first 方法,你可以遵循 指南prisma-binding 升级到 SDL-first 设置。

本指南还解释了如何从 JavaScript 迁移到 TypeScript,因此它基本上假设你对现有应用程序进行了完全重写。如果你想继续在 JavaScript 中运行你的应用程序,你可以忽略与 TypeScript 设置相关的说明,并像以前一样继续使用 JavaScript。

本指南假设你已经完成了 升级 Prisma ORM 层的指南。这意味着你已经

  • 安装了 Prisma ORM 2.0 CLI
  • 创建了你的 Prisma ORM 2.0 架构
  • 对你的数据库进行了内省并解决了潜在的 架构不兼容性
  • 安装并生成了 Prisma 客户端

本指南进一步假设你的文件设置类似于以下内容

.
├── 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 架构
  • 一个名为 src 的文件夹,其中包含你的应用程序代码和一个名为 schema.graphql 的架构

如果你的项目结构与此不符,则需要调整指南中的说明以匹配你自己的设置。

1. 安装和配置 Nexus

1.1. 安装 Nexus 依赖项

第一步是在你的项目中安装 Nexus 依赖项

npm install @nexus/schema

接下来,安装 Nexus 的 Prisma ORM 插件,这将允许你将 Prisma 模型暴露在你的 GraphQL API 中

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 的文件,该文件尚未在你的项目中。此文件将包含通过 GraphQL 解析器链传递的 context 对象的类型。

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
}

在接下来的步骤中,我们将解释如何将你使用 prisma-binding 实现的现有 SDL-first GraphQL 架构迁移到使用 Nexus 的等效设置。

2. 创建你的 GraphQL 类型

升级过程的下一步是创建你的GraphQL 类型。在本例中,你的 GraphQL 类型将反映 Prisma 模型(就像在你的 prisma-binding 设置中可能的情况一样)。如果 GraphQL 类型偏离 Prisma 模型,你就可以使用 Nexus API 轻松地调整暴露的 GraphQL 类型。

本指南中,您将把所有代码都放在同一个文件中。但是,您可以根据个人喜好构建文件并进行相应地 import

在 Nexus 中,GraphQL 类型通过 objectType 函数定义。导入 objectType,然后从第一个 GraphQL 类型框架开始。在本例中,我们从将 Prisma 架构的 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 架构的类型信息,并允许您通过 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 架构中。您可以通过将其添加到 types 中来完成,这些 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',
},
],
},
})

您可以在生成的 GraphQL 架构文件 ./schema.graphql 中的 SDL 中查看当前版本的 GraphQL 架构。

3. 迁移 GraphQL 操作

下一步,您可以开始将所有 GraphQL 查询变异从“以前的”GraphQL API 迁移到使用 Nexus 构建的新 API。

在本指南中,将使用以下样本 GraphQL 架构。

# 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 架构的 users 查询定义并实现如下。

带有 prisma-binding 的 SDL 架构定义
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 架构定义
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 的代码优先架构定义

要使用 Nexus 获得相同的行为,您需要在 queryType 中添加 t.field 定义。

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

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

虽然这段代码可能会在编辑器中导致类型错误,但您已经可以在 schema.graphql 中查看生成的 SDL 版本的 GraphQL 架构。您会注意到这已经将正确的定义添加到您的 GraphQL 架构中。

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 架构定义
type Query {
user(userUniqueInput: UserUniqueInput): User
# ... other queries
}

input UserUniqueInput {
id: String
email: String
}

请注意,这是一个比较牵强的例子,用于演示使用 nexusinput 类型。

带有 prisma-binding 的解析器实现
const resolvers = {
Query: {
user: (_, args, context, info) => {
return context.prisma.query.user(
{
where: args.userUniqueInput,
},
info
)
},
// ... other resolvers
},
}
带有 nexus 的代码优先架构定义

要使用 Nexus 获得相同的行为,您需要在 queryType 中添加 t.field 定义,并定义一个 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 架构中的一个新类型,因此您需要再次将其添加到传递给 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 中生成的 SDL 版本的 GraphQL 架构,您会注意到此更改已经将正确的定义添加到您的 GraphQL 架构中。

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: "[email protected]" }) {
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 变异

在本节中,您将从样本架构迁移 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
},
})

为了确保 Nexus 拾取新的 Mutation 类型,您需要将其添加到提供给 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 架构的 createUser 变异定义并实现如下。

带有 prisma-binding 的 SDL 架构定义
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 架构中变异的默认名称为 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: "[email protected]" }) {
id
}
}

如果您的应用程序使用 forwardTo 公开来自 Prisma Client 的所有 CRUD 操作,您现在可以使用相同的 t.crud 方法继续添加所有剩余的操作。要了解如何使用 Nexus 定义和解析“自定义”突变,请继续下一节。

3.2.3. 迁移 createDraft(title: String!, content: String, authorId: String!): Post! 查询

在示例应用程序中,createDraft 突变的定义和实现如下所示。

使用 prisma-binding 的 SDL 架构定义
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 的代码优先架构定义

要使用 Nexus 获得相同的行为,您需要在 mutationType 中添加一个 t.field 定义。

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 架构的 SDL 版本,您会注意到这已经将正确的定义添加到了您的 GraphQL 架构中。

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 架构定义
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 的代码优先架构定义

要使用 Nexus 获得相同的行为,您需要在 mutationType 中添加一个 t.field 定义。

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 架构的 SDL 版本,您会注意到这已经将正确的定义添加到了您的 GraphQL 架构中。

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: "[email protected]" }
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 架构定义
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 的代码优先架构定义

要使用 Nexus 获得相同的行为,您需要在 mutationType 中添加一个 t.field 定义。

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 架构的 SDL 版本,您会注意到这已经将正确的定义添加到了您的 GraphQL 架构中。

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 服务器。