从 prisma-binding 到 Nexus
概述
本指南并非完全最新,因为它目前使用的是已弃用的 已弃用 版本 nexus-plugin-prisma
。虽然它仍然可用,但建议未来使用新的 nexus-prisma
库或 Pothos 等其他代码优先的 GraphQL 库。如果您有任何问题,请加入我们的 Discord。
本升级指南介绍了如何迁移一个基于 Prisma 1 并使用 prisma-binding
实现 GraphQL 服务器的 Node.js 项目。
代码将迁移到 @nexus/schema
和 nexus-plugin-prisma
。与使用 prisma-binding
的 SDL 优先(SDL-first) 方法不同,Nexus 遵循代码优先(code-first)方法构建 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: http://localhost:4000`)
)
请注意,此设置已经包含 Nexus 的 Prisma ORM 插件配置。这将启用你将在本指南后面了解的 t.model
和 t.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: http://localhost:4000
你的 GraphQL 服务器现在正在 http://localhost:4000 运行。目前它实现了一个 GraphQL 查询,你可以如下发送:
{
hello
}
在接下来的步骤中,我们将解释如何将使用 prisma-binding
实现的现有 SDL 优先 GraphQL schema 迁移到使用 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 中。你可以通过将其添加到提供给 makeSchema
函数的 types
选项中来完成。
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',
},
],
},
})
你可以在生成的 GraphQL schema 文件 ./schema.graphql
中查看你的 GraphQL schema 的当前 SDL 版本。
3. 迁移 GraphQL 操作
下一步,你可以开始将所有 GraphQL 查询(queries)和变更(mutations)从“旧的” 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 查询
在本节中,你将把所有 GraphQL 查询从 prisma-binding
迁移到 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
的 Resolver 实现
const resolvers = {
Query: {
users: forwardTo('prisma'),
// ... other resolvers
},
}
为了在 Nexus 中实现相同的行为,你可以在 definition
函数内部使用 t
变量上的 crud
属性。
与 model
类似,此属性之所以可用,是因为你正在使用 nexus-prisma-plugin
,它利用了你的 Prisma 模型中的类型信息并在底层自动生成解析器。crud
属性也支持自动补全,因此你可以在编辑器中再次探索所有可用的查询。
使用 nexus-prisma-plugin
转发查询
要将 users
查询添加到你的 GraphQL API,将以下行添加到 query 类型定义中
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
的 Resolver 实现
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
的 Code-first schema 定义
为了在 Nexus 中获得相同的行为,你需要向 queryType
添加一个 t.field
定义
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
的 Resolver 实现
你可以使用 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
的 Resolver 实现
const resolvers = {
Query: {
user: (_, args, context, info) => {
return context.prisma.query.user(
{
where: args.userUniqueInput,
},
info
)
},
// ... other resolvers
},
}
使用 nexus
的 Code-first schema 定义
为了在 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 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
的 Code-first resolver 实现
那是因为你仍然缺少该查询的解析器实现。你可以使用 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 变更从示例 schema 迁移到 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
的 Resolver 实现
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,将以下行添加到 mutation 类型定义中
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
的 Resolver 实现
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
的 Code-first schema 定义
为了在 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 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
的 Resolver 实现
那是因为你仍然缺少该变更的解析器实现。你可以使用 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
的 Resolver 实现
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
的 Code-first schema 定义
为了在 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 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
的 Resolver 实现
那是因为你仍然缺少该查询的解析器实现。你可以使用 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
的 Resolver 实现
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
的 Code-first schema 定义
为了在 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 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
的 Resolver 实现
那是因为你仍然缺少该查询的解析器实现。你可以使用 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 服务器。