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-first* アプローチとは対照的に、Nexus はコードファーストのアプローチに従って GraphQL スキーマを構築します。これらの 2 つのアプローチの主な違いについては、この記事で学ぶことができます。SDL-first アプローチを引き続き使用する場合は、prisma-binding
から SDL-first セットアップにアップグレードするためのガイドに従うことができます。
このガイドでは、JavaScript から TypeScript への移行方法についても説明しており、既存のアプリの完全な書き換えを基本的に想定しています。JavaScript でアプリケーションを実行し続けたい場合は、TypeScript のセットアップに関連する手順を無視して、これまでどおり JavaScript を使用し続けることができます。
このガイドは、Prisma ORM レイヤーをアップグレードするためのガイドをすでに完了していることを前提としています。これは、すでに以下のことを行っていることを意味します。
- Prisma ORM 2.0 CLI をインストール済み
- Prisma ORM 2.0 スキーマを作成済み
- データベースをイントロスペクションし、潜在的なスキーマの非互換性を解決済み
- Prisma Client をインストールおよび生成済み
このガイドではさらに、次のようなファイル構成になっていることを前提としています。
.
├── README.md
├── package.json
├── prisma
│ └── schema.prisma
├── prisma1
│ ├── datamodel.prisma
│ └── prisma.yml
└── src
├── generated
│ └── prisma.graphql
├── index.js
└── schema.graphql
重要な部分は次のとおりです。
- Prisma ORM 2.0 スキーマを含む
prisma
という名前のフォルダー - アプリケーションコードと
schema.graphql
という名前のスキーマを含むsrc
という名前のフォルダー
プロジェクト構造がこれと異なる場合は、ガイドの手順を自分のセットアップに合わせて調整する必要があります。
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
ディレクトリ内に index.ts
という名前の API のルートソースファイルを作成します。
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://#: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: https://#:4000
GraphQL サーバーは現在 https://#: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
モデルの *フィールド* を 1 つずつ公開し始めることができます。エディターのオートコンプリートを使用すると、入力の手間を省くことができます。definition
関数の本文内で、t.model.
と入力し、CTRL+SPACE を押します。これにより、オートコンプリートが表示され、User
モデルで定義されているすべてのフィールドが提案されます。
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 エラーを引き起こす可能性があります(この場合、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 スキーマに手動で追加する必要もあります。これは、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',
},
],
},
})
最初の型が完了したら、残りの型を定義し始めることができます。
サンプルデータモデルの完全版を表示するには展開してください
すべてのサンプル Prisma モデルを 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,
})
},
})
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 スキーマファイルで、SDL の GraphQL スキーマの現在のバージョンを表示できます。
3. GraphQL 操作の移行
次のステップとして、「以前の」GraphQL API から Nexus で構築された新しい API に、すべての GraphQL *クエリ* と *ミューテーション* の移行を開始できます。
このガイドでは、次のサンプル 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
から Nexus にすべての GraphQL *クエリ* を移行します。
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
プロパティはオートコンプリートもサポートしているため、エディターで利用可能なすべてのクエリを再度調べることができます。
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
内の GraphQL スキーマの生成された SDL バージョンをすでに確認できます。これにより、GraphQL スキーマに正しい *定義* がすでに追加されていることがわかります。
type Query {
posts(searchString: String): [Post!]!
users(after: UserWhereUniqueInput, before: UserWhereUniqueInput, first: Int, last: Int, orderBy: Enumerable<UserOrderByInput>, skip: Int, where: UserWhereInput): [User!]!
}
ただし、コードには実際のリゾルバーロジックがありません。これは、次に追
Resolver implementation with nexus
You can add the resolver with Nexus as follows
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 },
},
],
},
})
},
})
},
})
To validate the implementation, you can now e.g. send the following example query to your GraphQL server
{
posts {
id
title
author {
id
name
}
}
}
3.1.2. Migrate the user(uniqueInput: UserUniqueInput): User
query
In our sample app, the user
query is defined and implemented as follows.
SDL schema definition with prisma-binding
type Query {
user(userUniqueInput: UserUniqueInput): User
# ... other queries
}
input UserUniqueInput {
id: String
email: String
}
Note that this is a bit of a contrived example to demonstrate the usage of input
types with Nexus.
Resolver implementation with prisma-binding
const resolvers = {
Query: {
user: (_, args, context, info) => {
return context.prisma.query.user(
{
where: args.userUniqueInput,
},
info
)
},
// ... other resolvers
},
}
Code-first schema definition with nexus
To get the same behavior with Nexus, you'll need to add a t.field
definition to the queryType
and define an inputObjectType
that includes the two @unique
fields of your User
model
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,
}),
},
})
},
})
Since UserUniqueInput
is a new type in your GraphQL schema, you again need to add it to the types
option that's passed to makeSchema
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',
},
],
},
})
If you look at the generated SDL version of your GraphQL schema inside schema.graphql
, you'll notice that this change already added the correct definition to your 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
}
You can even send the respective query via the GraphQL Playground already
{
user(userUniqueInput: { email: "alice@prisma.io" }) {
id
name
}
}
However, because the resolver is not yet implemented you will not get any data back yet.
Code-first resolver implementation with nexus
That's because you're still missing the resolver implementation for that query. You can add the resolver with Nexus as follows
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,
},
})
},
})
},
})
If you're re-sending the same query from before, you'll find that it now returns actual data.
3.2. Migrate GraphQL mutations
In this section, you'll migrate the GraphQL mutations from the sample schema to the Nexus.
3.2.1. Define the Mutation
type
The first step to migrate any mutations is to define the Mutation
type of your GraphQL API. Once that's done, you can gradually add operations to it. Add the following definition to index.ts
import { mutationType } from '@nexus/schema'
const Mutation = mutationType({
definition(t) {
// your GraphQL mutations + resolvers will be defined here
},
})
In order to make sure that the new Mutation
type is picked by up Nexus, you need to add it to the types
that are provided to makeSchema
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. Migrate the createUser
mutation (which uses forwardTo
)
In the sample app, the createUser
mutation from the sample GraphQL schema is defined and implemented as follows.
SDL schema definition with prisma-binding
type Mutation {
createUser(data: UserCreateInput!): User!
# ... other mutations
}
Resolver implementation with prisma-binding
const resolvers = {
Mutation: {
createUser: forwardTo('prisma'),
// ... other resolvers
},
}
Similar to forwarding GraphQL queries, you can use the crud
property on the t
variable inside the definition
function in order to expose full CRUD capabilities for Prisma models.
Similar to model
, this property is available because you're using the nexus-prisma-plugin
which leverages type information from your Prisma models and auto-generates resolvers under the hood. The crud
property supports autocompletion when defining mutations as well, so you can explore all available operations in your editor again
Forwarding the mutation with the nexus-prisma-plugin
To add the createUser
mutation to your GraphQL API, add the following lines to the query type definition
const Mutation = mutationType({
definition(t) {
t.crud.createOneUser({
alias: 'createUser',
})
},
})
Note that the default name for the mutation in your GraphQL schema is createOneUser
(named after the function which is exposed by t.crud
). In order to rename it to createUser
, you need to provide the alias
property.
If you have the Nexus development server running, you can save the file and your GraphQL API will be updated to expose the new createUser
mutation. You can also observe this by looking at the Mutation
type inside the generated schema.graphql
file
type Mutation {
createUser(data: UserCreateInput!): User!
}
You can now write your first mutation against the new API, e.g.
mutation {
createUser(data: { name: "Alice", email: "alice@prisma.io" }) {
id
}
}
If your application exposes all CRUD operations from Prisma Client using forwardTo
, you can now continue adding all remaining ones using the same approach via t.crud
. To learn how "custom" mutations can be defined and resolved using Nexus, move on to the next sections.
3.2.3. Migrate the createDraft(title: String!, content: String, authorId: String!): Post!
query
In the sample app, the createDraft
mutation is defined and implemented as follows.
SDL schema definition with prisma-binding
type Mutation {
createDraft(title: String!, content: String, authorId: String!): Post!
# ... other mutations
}
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
},
}
Code-first schema definition with nexus
To get the same behavior with Nexus, you'll need to add a t.field
definition to the mutationType
const Mutation = mutationType({
definition(t) {
// ... previous mutations
t.field('createDraft', {
type: 'Post',
args: {
title: stringArg({ nullable: false }),
content: stringArg(),
authorId: stringArg({ nullable: false }),
},
})
},
})
If you look at the generated SDL version of your GraphQL schema inside schema.graphql
, you'll notice that this has added the correct definition to your GraphQL schema already
type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: String!): Post!
}
You can even send the respective mutation via the GraphQL Playground already
mutation {
createDraft(title: "Hello World", authorId: "__AUTHOR_ID__") {
id
published
author {
id
name
}
}
}
However, because the resolver is not yet implemented, no new Post
record will be created and you will not get any data back in the response.
Resolver implementation with nexus
That's because you're still missing the resolver implementation for that mutation. You can add the resolver with Nexus as follows
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 },
},
},
})
},
})
},
})
If you're re-sending the same query from before, you'll find that it now create a new Post
record and return valid data.
3.2.4. Migrate the updateBio(bio: String, userUniqueInput: UserUniqueInput!): User
mutation
In the sample app, the updateBio
mutation is defined and implemented as follows.
SDL schema definition with prisma-binding
type Mutation {
updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User
# ... other mutations
}
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
},
}
nexus
を使用したコードファーストスキーマ定義
To get the same behavior with Nexus, you'll need to add a t.field
definition to the mutationType
const Mutation = mutationType({
definition(t) {
// ... previous mutations
t.field('updateBio', {
type: 'User',
args: {
userUniqueInput: arg({
type: 'UserUniqueInput',
nullable: false,
}),
bio: stringArg({ nullable: false }),
},
})
},
})
If you look at the generated SDL version of your GraphQL schema inside schema.graphql
, you'll notice that this has added the correct definition to your GraphQL schema already
type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: String!): Post!
updateBio(bio: String!, userUniqueInput: UserUniqueInput!): User
}
You can even send the respective mutation via the GraphQL Playground already
mutation {
updateBio(
userUniqueInput: { email: "alice@prisma.io" }
bio: "I like turtles"
) {
id
name
profile {
id
bio
}
}
}
ただし、リゾルバーはまだ実装されていないため、データベースは何も更新されず、レスポンスでデータは返ってきません。
nexus
を使用したリゾルバーの実装
That's because you're still missing the resolver implementation for that query. You can add the resolver with Nexus as follows
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
を使用したコードファーストスキーマ定義
To get the same behavior with Nexus, you'll need to add a t.field
definition to the 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,
}),
},
})
},
})
If you look at the generated SDL version of your GraphQL schema inside schema.graphql
, you'll notice that this has added the correct definition to your GraphQL schema already
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
}
You can even send the respective query via the GraphQL Playground already
mutation {
addPostToCategories(
postId: "__AUTHOR_ID__"
categoryIds: ["__CATEGORY_ID_1__", "__CATEGORY_ID_2__"]
) {
id
title
categories {
id
name
}
}
}
ただし、リゾルバーはまだ実装されていないため、データベースは何も更新されず、レスポンスでデータは返ってきません。
nexus
を使用したリゾルバーの実装
That's because you're still missing the resolver implementation for that query. You can add the resolver with Nexus as follows
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 サーバーの実行を停止できます。