Skip to main content

prisma 绑定到 SDL 优先

概述

¥Overview

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

¥This upgrade guide describes how to migrate a Node.js project that's based on Prisma 1 and uses prisma-binding to implement a GraphQL server.

该代码将保留 SDL 优先方法 用于构建 GraphQL 模式。从 prisma-binding 迁移到 Prisma Client 时,主要区别在于 info 对象不能再用于自动解析关系,而是需要实现类型解析器以确保关系得到正确解析。

¥The code will keep the SDL-first approach for constructing the GraphQL schema. When migrating from prisma-binding to Prisma Client, the main difference is that the info object can't be used to resolve relations automatically any more, instead you'll need to implement your type resolvers to ensure that relations get resolved properly.

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

¥The guide assumes that you already went through the guide for upgrading the Prisma ORM layer. This means you already:

  • 安装 Prisma ORM 2 CLI

    ¥installed the Prisma ORM 2 CLI

  • 创建了你的 Prisma ORM 2 架构

    ¥created your Prisma ORM 2 schema

  • 内省你的数据库并解决潜在的架构不兼容性

    ¥introspected your database and resolved potential schema incompatibilities

  • 安装并生成 Prisma 客户端

    ¥installed and generated Prisma Client

该指南进一步假设你有一个与此类似的文件设置:

¥The guide further assumes that you have a file setup that looks similar to this:

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

重要的部分是:

¥The important parts are:

  • 使用 Prisma ORM 2 架构以 prisma 调用的文件夹

    ¥A folder called with prisma with your Prisma ORM 2 schema

  • 名为 src 的文件夹,其中包含你的应用代码和名为 schema.graphql 的架构

    ¥A folder called src with your application code and a schema called schema.graphql

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

¥If this is not what your project structure looks like, you'll need to adjust the instructions in the guide to match your own setup.

1. 调整你的 GraphQL 架构

¥ Adjusting your GraphQL schema

使用 prisma-binding,定义 GraphQL 架构(有时称为 应用模式)的方法基于从生成的 prisma.graphql 文件导入 GraphQL 类型(在 Prisma 1 中,这通常称为 Prisma GraphQL 架构)。这些类型反映了 Prisma 1 数据模型中的类型,并作为 GraphQL API 的基础。

¥With prisma-binding, your approach for defining your GraphQL schema (sometimes called application schema) is based on importing GraphQL types from the generated prisma.graphql file (in Prisma 1, this is typically called Prisma GraphQL schema). These types mirror the types from your Prisma 1 datamodel and serve as foundation for your GraphQL API.

使用 Prisma ORM 2,你不再可以导入 prisma.graphql 文件。因此,你必须直接在 schema.graphql 文件中拼出 GraphQL 模式的所有类型。

¥With Prisma ORM 2, there's no prisma.graphql file any more that you could import from. Therefore, you have to spell out all the types of your GraphQL schema directly inside your schema.graphql file.

最简单的方法是从 GraphQL Playground 下载完整的 GraphQL 架构。为此,请打开 SCHEMA 选项卡并单击右上角的下载按钮,然后选择 SDL:

¥The easiest way to do so is by downloading the full GraphQL schema from the GraphQL Playground. To do so, open the SCHEMA tab and click the DOWNLOAD button in the top-right corner, then select SDL:

Downloading the GraphQL schema with GraphQL Playground

或者,你可以使用 GraphQL CLIget-schema 命令下载完整架构:

¥Alternatively, you can use the get-schema command of the GraphQL CLI to download your full schema:

npx graphql get-schema --endpoint __GRAPHQL_YOGA_ENDPOINT__ --output schema.graphql --no-all

注意:使用上述命令,你需要将 __GRAPHQL_YOGA_ENDPOINT__ 占位符替换为 GraphQL Yoga 服务器的实际端点。

¥Note: With the above command, you need to replace the __GRAPHQL_YOGA_ENDPOINT__ placeholder with the actual endpoint of your GraphQL Yoga server.

获得 schema.graphql 文件后,用新内容替换 src/schema.graphql 中的当前版本。请注意,这两个架构 100% 等效,只是新架构不使用 graphql-import 从不同文件导入类型。相反,它在一个文件中详细说明了所有类型。

¥Once you obtained the schema.graphql file, replace your current version in src/schema.graphql with the new contents. Note that the two schemas are 100% equivalent, except that the new one doesn't use graphql-import for importing types from a different file. Instead, it spells out all types in a single file.

以下是我们将在本指南中迁移的示例 GraphQL 模式的这两个版本的比较(你可以使用选项卡在两个版本之间切换):

¥Here's a comparison of these two versions of the sample GraphQL schema that we'll migrate in this guide (you can use the tabs to switch between the two versions):

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

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]!
allCategories: [Category!]!
}

input UserUniqueInput {
id: String
email: String
}

type Mutation {
createDraft(authorId: ID!, title: String!, content: String!): Post
publish(id: ID!): Post
deletePost(id: ID!): Post
signup(name: String!, email: String!): User!
updateBio(userId: String!, bio: String!): User
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
}

你会注意到,新版本的 GraphQL 架构不仅定义了直接导入的模型,还定义了之前架构中不存在的其他类型(例如 input 类型)。

¥You'll notice that the new version of your GraphQL schema not only defines the models that were imported directly, but also additional types (e.g. input types) that were not present in the schema before.

2. 设置你的 PrismaClient 实例

¥ Set up your PrismaClient instance

PrismaClient 是 Prisma ORM 2 中数据库的新接口。它允许你调用各种方法来构建 SQL 查询并将其发送到数据库,以纯 JavaScript 对象的形式返回结果。

¥PrismaClient is your new interface to the database in Prisma ORM 2. It lets you invoke various methods which build SQL queries and send them to the database, returning the results as plain JavaScript objects.

PrismaClient 查询 API 的灵感来自最初的 prisma-binding API,因此你使用 Prisma Client 发送的许多查询都会感到熟悉。

¥The PrismaClient query API is inspired by the initial prisma-binding API, so a lot of the queries you send with Prisma Client will feel familiar.

与 Prisma 1 中的 prisma-binding 实例类似,你还希望将 Prisma ORM 2 中的 PrismaClient 连接到 GraphQL 的 context,以便可以在解析器内部访问:

¥Similar to the prisma-binding instance from Prisma 1, you also want to attach your PrismaClient from Prisma ORM 2 to GraphQL's context so that in can be accessed inside your resolvers:

const { PrismaClient } = require('@prisma/client')

// ...

const server = new GraphQLServer({
typeDefs: 'src/schema.graphql',
resolvers,
context: (req) => ({
...req,
prisma: new Prisma({
typeDefs: 'src/generated/prisma.graphql',
endpoint: 'http://localhost:4466',
}),
prisma: new PrismaClient(),
}),
})

在上面的代码块中,红行是要从当前设置中删除的行,绿行是你应该添加的行。当然,你之前的设置可能与此不同(例如,如果你在生产中运行 API,则你的 Prisma ORM endpoint 不太可能是 http://localhost:4466),这只是一个示例,用于指示它的外观。

¥In the code block above, the red lines are the lines to be removed from your current setup, the green lines are the ones that you should add. Of course, it's possible that your previous setup differed from this one (e.g. it's unlikely that your Prisma ORM endpoint was http://localhost:4466 if you're running your API in production), this is just a sample to indicate what it could look like.

当你现在访问解析器内部的 context.prisma 时,你现在可以访问 Prisma 客户端查询。

¥When you're now accessing context.prisma inside of a resolver, you now have access to Prisma Client queries.

2. 编写 GraphQL 类型解析器

¥ Write your GraphQL type resolvers

prisma-binding 能够神奇地解析 GraphQL 模式中的关系。但是,当不使用 prisma-binding 时,你需要使用所谓的类型解析器显式解析你的关系。

¥prisma-binding was able to magically resolve relations in your GraphQL schema. When not using prisma-binding though, you need to explicitly resolve your relations using so-called type resolvers.

注意 你可以在本文中了解有关类型解析器的概念以及为什么需要它们的更多信息:GraphQL 服务器基础知识:GraphQL 模式、TypeDef 和解析器解释

¥You can learn more about the concept of type resolvers and why they're necessary in this article: GraphQL Server Basics: GraphQL Schemas, TypeDefs & Resolvers Explained

2.1.实现 User 类型的类型解析器

¥2.1. Implementing the type resolver for the User type

我们的示例 GraphQL 架构中的 User 类型定义如下:

¥The User type in our sample GraphQL schema is defined as follows:

type User implements Node {
id: ID!
email: String
name: String!
posts(
where: PostWhereInput
orderBy: Enumerable<PostOrderByInput>
skip: Int
after: String
before: String
first: Int
last: Int
): [Post!]
role: Role!
profile: Profile
jsonData: Json
}

这个类型有两个关系:

¥This type has two relations:

  • posts 字段表示与 Post 的 1-n 关系

    ¥The posts field denotes a 1-n relation to Post

  • profile 字段表示与 Profile 的 1-1 关系

    ¥The profile field denotes a 1-1 relation to Profile

由于你不再使用 prisma-binding,因此你现在需要在类型解析器中解析这些关系 "manually"。

¥Since you're not using prisma-binding any more, you now need to resolve these relations "manually" in type resolvers.

你可以通过向解析器映射添加 User 字段并实现 postsprofile 关系的解析器来实现此目的,如下所示:

¥You can do so by adding a User field to your resolver map and implement the resolvers for the posts and profile relations as follows:

const resolvers = {
Query: {
// ... your query resolvers
},
Mutation: {
// ... your mutation resolvers
},
User: {
posts: (parent, args, context) => {
return context.prisma.user
.findUnique({
where: { id: parent.id },
})
.posts()
},
profile: (parent, args, context) => {
return context.prisma.user
.findUnique({
where: { id: parent.id },
})
.profile()
},
},
}

在这些解析器内部,你使用新的 PrismaClient 对数据库执行查询。在 posts 解析器内部,数据库查询加载指定 author 中的所有 Post 记录(其 id 包含在 parent 对象中)。在 profile 解析器内部,数据库查询从指定的 user(其 idparent 对象中携带)加载 Profile 记录。

¥Inside of these resolvers, you're using your new PrismaClient to perform a query against the database. Inside the posts resolver, the database query loads all Post records from the specified author (whose id is carried in the parent object). Inside the profile resolver, the database query loads the Profile record from the specified user (whose id is carried in the parent object).

由于这些额外的解析器,现在每当你在查询中请求有关 User 类型的信息时,你现在都可以在 GraphQL 查询/突变中嵌套关系,例如:

¥Thanks to these extra resolvers, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the User type in a query, e.g.:

{
users {
id
name
posts {
# fetching this relation is enabled by the new type resolver
id
title
}
profile {
# fetching this relation is enabled by the new type resolver
id
bio
}
}
}

2.2.实现 Post 类型的类型解析器

¥2.2. Implementing the type resolver for the Post type

我们的示例 GraphQL 架构中的 Post 类型定义如下:

¥The Post type in our sample GraphQL schema is defined as follows:

type Post implements Node {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
title: String!
content: String
published: Boolean!
author: User
categories(
where: CategoryWhereInput
orderBy: Enumerable<CategoryOrderByInput>
skip: Int
after: String
before: String
first: Int
last: Int
): [Category!]
}

这个类型有两个关系:

¥This type has two relations:

  • author 字段表示与 User 的 1-n 关系

    ¥The author field denotes a 1-n relation to User

  • categories 字段表示与 Category 的 m-n 关系

    ¥The categories field denotes a m-n relation to Category

由于你不再使用 prisma-binding,因此你现在需要在类型解析器中解析这些关系 "manually"。

¥Since you're not using prisma-binding any more, you now need to resolve these relations "manually" in type resolvers.

你可以通过向解析器映射添加 Post 字段并实现 authorcategories 关系的解析器来实现此目的,如下所示:

¥You can do so by adding a Post field to your resolver map and implement the resolvers for the author and categories relations as follows:

const resolvers = {
Query: {
// ... your query resolvers
},
Mutation: {
// ... your mutation resolvers
},
User: {
// ... your type resolvers for `User` from before
},
Post: {
author: (parent, args, context) => {
return context.prisma.post
.findUnique({
where: { id: parent.id },
})
.author()
},
categories: (parent, args, context) => {
return context.prisma.post
.findUnique({
where: { id: parent.id },
})
.categories()
},
},
}

在这些解析器内部,你使用新的 PrismaClient 对数据库执行查询。在 author 解析器内,数据库查询加载代表 PostauthorUser 记录。在 categories 解析器内部,数据库查询加载指定 post 中的所有 Category 记录(其 id 包含在 parent 对象中)。

¥Inside of these resolvers, you're using your new PrismaClient to perform a query against the database. Inside the author resolver, the database query loads the User record that represents the author of the Post. Inside the categories resolver, the database query loads all Category records from the specified post (whose id is carried in the parent object).

由于这些额外的解析器,现在每当你在查询中请求有关 User 类型的信息时,你现在都可以在 GraphQL 查询/突变中嵌套关系,例如:

¥Thanks to these extra resolvers, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the User type in a query, e.g.:

{
posts {
id
title
author {
# fetching this relation is enabled by the new type resolver
id
name
}
categories {
# fetching this relation is enabled by the new type resolver
id
name
}
}
}

2.3.实现 Profile 类型的类型解析器

¥2.3. Implementing the type resolver for the Profile type

我们的示例 GraphQL 架构中的 Profile 类型定义如下:

¥The Profile type in our sample GraphQL schema is defined as follows:

type Profile implements Node {
id: ID!
bio: String
user: User!
}

这种类型有一个关系:user 字段表示与 User 的 1-n 关系。

¥This type has one relation: The user field denotes a 1-n relation to User.

由于你不再使用 prisma-binding,因此你现在需要在类型解析器中解析此关系 "manually"。

¥Since you're not using prisma-binding any more, you now need to resolve this relation "manually" in type resolvers.

你可以通过将 Profile 字段添加到解析器映射并实现 owner 关系的解析器来实现此目的,如下所示:

¥You can do so by adding a Profile field to your resolver map and implement the resolvers for the owner relation as follows:

const resolvers = {
Query: {
// ... your query resolvers
},
Mutation: {
// ... your mutation resolvers
},
User: {
// ... your type resolvers for `User` from before
},
Post: {
// ... your type resolvers for `Post` from before
},
Profile: {
user: (parent, args, context) => {
return context.prisma.profile
.findUnique({
where: { id: parent.id },
})
.owner()
},
},
}

在此解析器内部,你使用新的 PrismaClient 对数据库执行查询。在 user 解析器内部,数据库查询从指定的 profile(其 idparent 对象中携带)加载 User 记录。

¥Inside of this resolver, you're using your new PrismaClient to perform a query against the database. Inside the user resolver, the database query loads the User records from the specified profile (whose id is carried in the parent object).

借助这个额外的解析器,无论何时你在查询中请求有关 Profile 类型的信息,你现在都可以在 GraphQL 查询/突变中嵌套关系。

¥Thanks to this extra resolver, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about the Profile type in a query.

2.4.实现 Category 类型的类型解析器

¥2.4. Implementing the type resolver for the Category type

我们的示例 GraphQL 架构中的 Category 类型定义如下:

¥The Category type in our sample GraphQL schema is defined as follows:

type Category implements Node {
id: ID!
name: String!
posts(
where: PostWhereInput
orderBy: Enumerable<PostOrderByInput>
skip: Int
after: String
before: String
first: Int
last: Int
): [Post!]
}

这种类型有一个关系:posts 字段表示与 Post 的 m-n 关系。

¥This type has one relation: The posts field denotes a m-n relation to Post.

由于你不再使用 prisma-binding,因此你现在需要在类型解析器中解析此关系 "manually"。

¥Since you're not using prisma-binding any more, you now need to resolve this relation "manually" in type resolvers.

你可以通过向解析器映射添加 Category 字段并实现 postsprofile 关系的解析器来实现此目的,如下所示:

¥You can do so by adding a Category field to your resolver map and implement the resolvers for the posts and profile relations as follows:

const resolvers = {
Query: {
// ... your query resolvers
},
Mutation: {
// ... your mutation resolvers
},
User: {
// ... your type resolvers for `User` from before
},
Post: {
// ... your type resolvers for `Post` from before
},
Profile: {
// ... your type resolvers for `User` from before
},
Category: {
posts: (parent, args, context) => {
return context.prisma
.findUnique({
where: { id: parent.id },
})
.posts()
},
},
}

在此解析器内部,你使用新的 PrismaClient 对数据库执行查询。在 posts 解析器内部,数据库查询加载指定 categories 中的所有 Post 记录(其 id 包含在 parent 对象中)。

¥Inside of this resolver, you're using your new PrismaClient to perform a query against the database. Inside the posts resolver, the database query loads all Post records from the specified categories (whose id is carried in the parent object).

借助这个额外的解析器,无论何时你在查询中请求有关 Category 类型的信息,你现在都可以在 GraphQL 查询/突变中嵌套关系。

¥Thanks to this extra resolver, you'll now be able to nest relations in your GraphQL queries/mutations whenever you're requesting information about a Category type in a query.

所有类型解析器就位后,你就可以开始迁移实际的 GraphQL API 操作。

¥With all your type resolvers in place, you can start migrating the actual GraphQL API operations.

3. 迁移 GraphQL 操作

¥ Migrate GraphQL operations

3.1.迁移 GraphQL 查询

¥3.1. Migrate GraphQL queries

在本部分中,你将把所有 GraphQL 查询从 prisma-binding 迁移到 Prisma 客户端。

¥In this section, you'll migrate all GraphQL queries from prisma-binding to Prisma Client.

3.1.1.迁移 users 查询(使用 forwardTo

¥3.1.1. Migrate the users query (which uses forwardTo)

在我们的示例 API 中,示例 GraphQL 架构中的 users 查询的定义和实现如下。

¥In our sample API, the users query from the sample GraphQL schema is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Query {
users(where: UserWhereInput, orderBy: Enumerable<UserOrderByInput>, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
# ... other queries
}
使用 prisma-binding 实现解析器

¥Resolver implementation with prisma-binding

const resolvers = {
Query: {
users: forwardTo('prisma'),
// ... other resolvers
},
}
使用 Prisma 客户端实现 users 解析器

¥Implementing the users resolver with Prisma Client

要重新实现之前使用 forwardTo 的查询,想法是将传入的过滤、排序和分页参数传递给 PrismaClient

¥To re-implement queries that were previously using forwardTo, the idea is to pass the incoming filtering, ordering and pagination arguments to PrismaClient:

const resolvers = {
Query: {
users: (_, args, context, info) => {
// this doesn't work yet
const { where, orderBy, skip, first, last, after, before } = args
return context.prisma.user.findMany({
where,
orderBy,
skip,
first,
last,
after,
before,
})
},
// ... other resolvers
},
}

请注意,这种方法还不起作用,因为传入参数的结构与 PrismaClient 期望的结构不同。为了确保结构兼容,你可以使用 @prisma/binding-argument-transform npm 包来确保兼容性:

¥Note that this approach does not work yet because the structures of the incoming arguments is different from the ones expected by PrismaClient. To ensure the structures are compatible, you can use the @prisma/binding-argument-transform npm package which ensures compatibility:

npm install @prisma/binding-argument-transform

你现在可以按如下方式使用该包:

¥You can now use this package as follows:

const {
makeOrderByPrisma2Compatible,
makeWherePrisma2Compatible,
} = require('@prisma/binding-argument-transform')

const resolvers = {
Query: {
users: (_, args, context, info) => {
// this still doesn't entirely work
const { where, orderBy, skip, first, last, after, before } = args
const prisma2Where = makeWherePrisma2Compatible(where)
const prisma2OrderBy = makeOrderByPrisma2Compatible(orderBy)
return context.prisma.user.findMany({
where: prisma2Where,
orderBy: prisma2OrderBy,
skip,
first,
last,
after,
before,
})
},
// ... other resolvers
},
}

最后剩下的问题是分页参数。Prisma ORM 2 引入了 新的分页 API

¥The last remaining issue with this are the pagination arguments. Prisma ORM 2 introduces a new pagination API:

  • 删除了 firstlastbeforeafter 参数

    ¥The first, last, before and after arguments are removed

  • 新的 cursor 参数取代了 beforeafter

    ¥The new cursor argument replaces before and after

  • 新的 take 参数取代了 firstlast

    ¥The new take argument replaces first and last

以下是你可以调整调用以使其符合新的 Prisma 客户端分页 API 的方法:

¥Here is how you can adjust the call to make it compliant with the new Prisma Client pagination API:

const {
makeOrderByPrisma2Compatible,
makeWherePrisma2Compatible,
} = require('@prisma/binding-argument-transform')

const resolvers = {
Query: {
users: (_, args, context) => {
const { where, orderBy, skip, first, last, after, before } = args
const prisma2Where = makeWherePrisma2Compatible(where)
const prisma2OrderBy = makeOrderByPrisma2Compatible(orderBy)
const skipValue = skip || 0
const prisma2Skip = Boolean(before) ? skipValue + 1 : skipValue
const prisma2Take = Boolean(last) ? -last : first
const prisma2Before = { id: before }
const prisma2After = { id: after }
const prisma2Cursor =
!Boolean(before) && !Boolean(after)
? undefined
: Boolean(before)
? prisma2Before
: prisma2After
return context.prisma.user.findMany({
where: prisma2Where,
orderBy: prisma2OrderBy,
skip: prisma2Skip,
cursor: prisma2Cursor,
take: prisma2Take,
})
},
// ... other resolvers
},
}

需要进行计算以确保传入的分页参数正确映射到 Prisma 客户端 API 中的分页参数。

¥The calculations are needed to ensure the incoming pagination arguments map properly to the ones from the Prisma Client API.

3.1.2.迁移 posts(searchString: String): [Post!]! 查询

¥3.1.2. Migrate the posts(searchString: String): [Post!]! query

posts 查询的定义和实现如下。

¥The posts query is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Query {
posts(searchString: String): [Post!]!
# ... other queries
}
使用 prisma-binding 实现解析器

¥Resolver implementation with 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
},
}
使用 Prisma 客户端实现 posts 解析器

¥Implementing the posts resolver with Prisma Client

要在新的 Prisma 客户端中获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Query: {
posts: (_, args, context) => {
return context.prisma.post.findMany({
where: {
OR: [
{ title: { contains: args.searchString } },
{ content: { contains: args.searchString } },
],
},
})
},
// ... other resolvers
},
}

你现在可以在 GraphQL Playground 中发送相应的查询:

¥You can now send the respective query in the GraphQL Playground:

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

3.1.3.迁移 user(uniqueInput: UserUniqueInput): User 查询

¥3.1.3. Migrate the user(uniqueInput: UserUniqueInput): User query

在我们的示例应用中,user 查询的定义和实现如下。

¥In our sample app, the user query is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Query {
user(userUniqueInput: UserUniqueInput): User
# ... other queries
}

input UserUniqueInput {
id: String
email: String
}
使用 prisma-binding 实现解析器

¥Resolver implementation with prisma-binding

const resolvers = {
Query: {
user: (_, args, context, info) => {
return context.prisma.query.user(
{
where: args.userUniqueInput,
},
info
)
},
// ... other resolvers
},
}
使用 Prisma 客户端实现 user 解析器

¥Implementing the user resolver with Prisma Client

要在新的 Prisma 客户端中获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Query: {
user: (_, args, context) => {
return context.prisma.user.findUnique({
where: args.userUniqueInput,
})
},
// ... other resolvers
},
}

你现在可以通过 GraphQL Playground 发送相应的查询:

¥You can now send the respective query via the GraphQL Playground:

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

3.1.迁移 GraphQL 突变

¥3.1. Migrate GraphQL mutations

在本部分中,你将从示例架构中迁移 GraphQL 突变。

¥In this section, you'll migrate the GraphQL mutations from the sample schema.

3.1.2.迁移 createUser 突变(使用 forwardTo

¥3.1.2. Migrate the createUser mutation (which uses forwardTo)

在示例应用中,示例 GraphQL 架构中的 createUser 突变的定义和实现如下。

¥In the sample app, the createUser mutation from the sample GraphQL schema is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Mutation {
createUser(data: UserCreateInput!): User!
# ... other mutations
}
使用 prisma-binding 实现解析器

¥Resolver implementation with prisma-binding

const resolvers = {
Mutation: {
createUser: forwardTo('prisma'),
// ... other resolvers
},
}
使用 Prisma 客户端实现 createUser 解析器

¥Implementing the createUser resolver with Prisma Client

要在新的 Prisma 客户端中获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Mutation: {
createUser: (_, args, context, info) => {
return context.prisma.user.create({
data: args.data,
})
},
// ... other resolvers
},
}

你现在可以针对新 API 编写第一个突变,例如:

¥You can now write your first mutation against the new API, e.g.:

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

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

¥3.1.3. Migrate the createDraft(title: String!, content: String, authorId: String!): Post! query

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

¥In the sample app, the createDraft mutation is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Mutation {
createDraft(title: String!, content: String, authorId: String!): Post!
# ... other mutations
}
使用 prisma-binding 实现解析器

¥Resolver implementation with 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
},
}
使用 Prisma 客户端实现 createDraft 解析器

¥Implementing the createDraft resolver with Prisma Client

要在新的 Prisma 客户端中获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with the new Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Mutation: {
createDraft: (_, args, context, info) => {
return context.prisma.post.create({
data: {
title: args.title,
content: args.content,
author: {
connect: {
id: args.authorId,
},
},
},
})
},
// ... other resolvers
},
}

你现在可以通过 GraphQL Playground 发送相应的突变:

¥You can now send the respective mutation via the GraphQL Playground:

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

3.1.4.迁移 updateBio(bio: String, userUniqueInput: UserUniqueInput!): User 突变

¥3.1.4. Migrate the updateBio(bio: String, userUniqueInput: UserUniqueInput!): User mutation

在示例应用中,updateBio 突变的定义和实现如下。

¥In the sample app, the updateBio mutation is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Mutation {
updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User
# ... other mutations
}
使用 prisma-binding 实现解析器

¥Resolver implementation with 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
},
}
使用 Prisma 客户端实现 updateBio 解析器

¥Implementing the updateBio resolver with Prisma Client

要使用 Prisma Client 获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Mutation: {
updateBio: (_, args, context, info) => {
return context.prisma.user.update({
data: {
profile: {
update: { bio: args.bio },
},
},
where: args.userUniqueInput,
})
},
// ... other resolvers
},
}

你现在可以通过 GraphQL Playground 发送相应的突变:

¥You can now send the respective mutation via the GraphQL Playground :

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

3.1.5.迁移 addPostToCategories(postId: String!, categoryIds: [String!]!): Post 突变

¥3.1.5. Migrate the addPostToCategories(postId: String!, categoryIds: [String!]!): Post mutation

在我们的示例应用中,addPostToCategories 突变的定义和实现如下。

¥In our sample app, the addPostToCategories mutation is defined and implemented as follows.

使用 prisma-binding 的 SDL 架构定义

¥SDL schema definition with prisma-binding

type Mutation {
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
# ... other mutations
}
使用 prisma-binding 实现解析器

¥Resolver implementation with 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
},
}
使用 Prisma 客户端实现 addPostToCategories 解析器

¥Implementing the addPostToCategories resolver with Prisma Client

要使用 Prisma Client 获得相同的行为,你需要调整解析器实现:

¥To get the same behavior with Prisma Client, you'll need to adjust your resolver implementation:

const resolvers = {
Mutation: {
addPostToCategories: (_, args, context, info) => {
const ids = args.categoryIds.map((id) => ({ id }))
return context.prisma.post.update({
where: {
id: args.postId,
},
data: {
categories: { connect: ids },
},
})
},
// ... other resolvers
},
}

你现在可以通过 GraphQL Playground 发送相应的查询:

¥You can now send the respective query via the GraphQL Playground:

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

4. 打扫干净

¥ Cleaning up

由于整个应用现已升级到 Prisma ORM 2,你可以删除所有不必要的文件并删除不再需要的依赖。

¥Since the entire app has now been upgraded to Prisma ORM 2, you can delete all unnecessary files and remove the no longer needed dependencies.

4.1.清理 npm 依赖

¥4.1. Clean up npm dependencies

你可以首先删除与 Prisma 1 设置相关的 npm 依赖:

¥You can start by removing npm dependencies that were related to the Prisma 1 setup:

npm uninstall graphql-cli prisma-binding prisma1

4.2.删除未使用的文件

¥4.2. Delete unused files

接下来,删除 Prisma 1 设置的文件:

¥Next, delete the files of your Prisma 1 setup:

rm prisma1/datamodel.prisma prisma1/prisma.yml

4.3.停止 Prisma ORM 服务器

¥4.3. Stop the Prisma ORM server

最后,你可以停止运行 Prisma ORM 服务器。

¥Finally, you can stop running your Prisma ORM server.