メインコンテンツにスキップ

古い Nexus から新しい Nexus へ

概要

情報

このガイドは、現在 非推奨 バージョンの nexus-plugin-prisma を使用しているため、完全に最新ではありません。これはまだ機能しますが、新しい nexus-prisma ライブラリまたは Pothos のような代替のコードファースト GraphQL ライブラリを今後使用することをお勧めします。ご不明な点がございましたら、Discord でお気軽にご質問ください。

このアップグレードガイドでは、GraphQL サーバーを実装するために、Prisma 1nexus (< v0.12.0) または @nexus/schemanexus-prisma (< v4.0.0) をベースにしているプロジェクトをアップグレードする方法について説明します。

コードは @nexus/schema の最新バージョンにアップグレードされます。さらに、nexus-prisma パッケージは、新しい nexus-plugin-prisma に置き換えられます。

このガイドは、Prisma ORM レイヤーをアップグレードするためのガイドをすでに完了していることを前提としています。これは、すでに以下の作業が完了していることを意味します。

  • Prisma ORM 2 CLI をインストール済み
  • Prisma ORM 2 スキーマを作成済み
  • データベースをイントロスペクションし、潜在的な スキーマの非互換性 を解決済み
  • Prisma Client をインストールおよび生成済み

このガイドはさらに、次のようなファイル構成になっていることを前提としています。

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

重要な部分は次のとおりです。

  • Prisma ORM 2 スキーマを含む prisma という名前のフォルダ
  • アプリケーションコードを含む src という名前のフォルダ

プロジェクト構造がこれと異なる場合は、ガイドの手順を自身のセットアップに合わせて調整する必要があります。

1. Nexus 依存関係のアップグレード

まず、古い Nexus と Prisma 1 の依存関係を削除します。

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

次に、最新の @nexus/schema 依存関係をプロジェクトにインストールします。

npm install @nexus/schema

次に、GraphQL API で Prisma ORM モデルを公開できるようにする Nexus 用の Prisma ORM プラグインをインストールします(これは、以前の nexus-prisma パッケージの新しい同等品です)。

npm install nexus-plugin-prisma

nexus-plugin-prisma 依存関係は、必要なすべての Prisma ORM 依存関係をバンドルします。したがって、アプリの Prisma ORM レイヤーをアップグレードしたときに追加インストールした依存関係を削除する必要があります。

npm uninstall @prisma/cli @prisma/client

ただし、おなじみのコマンドで Prisma ORM 2 CLI を引き続き呼び出すことができることに注意してください。

npx prisma -v

: npx prisma -v を実行したときに Prisma 1 CLI の出力が表示される場合は、必ず node_modules フォルダを削除して npm install を再実行してください。

2. Nexus と Prisma ORM の構成の更新

まず、新しいセットアップで不要になった古いインポートを削除します。

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

代わりに、アプリケーションに以下をインポートするようになりました。

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

次に、現在 GraphQLSchema を作成しているコードを調整する必要があります。おそらく現在、コード内の makePrismaSchema 関数を使用して行われています。この関数は削除された nexus-prisma パッケージからインポートされたため、@nexus/schema パッケージの makeSchema 関数に置き換える必要があります。Nexus 用 Prisma ORM プラグインの使用方法も最新バージョンで変更されています。

そのような構成の例を次に示します。

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

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

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

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

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

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

以前にリゾルバーチェーンを介して渡される GraphQL context オブジェクトの型を指定していた場合は、次のように型を調整する必要があります。

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

export interface Context {
prisma: Prisma
prisma: PrismaClient
}

3. GraphQL 型の移行

@nexus/schemanexus-plugin-prisma の最新バージョンで GraphQL 型を作成する 2 つのアプローチの主な違いの概要を次に示します。

  • prismaObjectType 関数はもう利用できません。すべての型は Nexus の objectType 関数で作成されます。
  • Nexus 経由で Prisma モデルを公開するには、Nexus の definition 関数に渡される t 引数に追加される t.model プロパティを使用できます。t.model を使用すると、Prisma モデルのプロパティにアクセスして公開できます。
  • Nexus 経由で Prisma モデルの CRUD 操作を公開する場合も同様のアプローチに従います。これらは、queryType 型と mutationType 型の definition 関数で t.crud 経由で公開されます。

3.1. Post 型の移行

以前の nexus-prisma パッケージを使用した型の定義

サンプルアプリでは、User 型は次のように定義されています。

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

@nexus/schemanexus-plugin-prisma の最新バージョンを使用した型の定義

@nexus/schema の最新バージョンでは、メインの schema インスタンスで objectType 関数にアクセスし、次のように Prisma モデルからすべてのフィールドを公開できます。

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

t.model は、objectType 関数に引数として渡されるオブジェクトの name 属性を参照し、Prisma スキーマ内のモデルと照合することに注意してください。この場合、User モデルと照合されます。したがって、t.modelUser モデルのフィールドの名前が付けられた関数を公開します。

この時点で、リレーションフィールド postsprofile でエラーが発生する可能性があります。例:

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

これは、Post 型と Profile 型を GraphQL スキーマにまだ追加していないためです。これらの型が GraphQL スキーマの一部になると、エラーは消えます!

3.2. Post 型の移行

以前の nexus-prisma パッケージを使用した型の定義

サンプルアプリでは、Post 型は次のように定義されています。

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

prismaFields のアスタリスクは、すべて の Prisma フィールドが公開されていることを意味します。

@nexus/schemanexus-plugin-prisma の最新バージョンを使用した型の定義

@nexus/schema の最新バージョンでは、すべてのフィールドを明示的に公開する必要があります。Prisma モデルからすべてを公開するオプションはありません。

したがって、Post の新しい定義では、すべてのフィールドを明示的にリストする必要があります。

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

t.model は、name 属性を参照し、Prisma スキーマ内のモデルと照合することに注意してください。この場合、Post モデルと照合されます。したがって、t.modelPost モデルのフィールドの名前が付けられた関数を公開します。

3.3. Profile 型の移行

以前の nexus-prisma パッケージを使用した型の定義

サンプルアプリでは、Profile 型は次のように定義されています。

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

prismaFields のアスタリスクは、すべて の Prisma フィールドが公開されていることを意味します。

@nexus/schemanexus-plugin-prisma の最新バージョンを使用した型の定義

@nexus/schema の最新バージョンでは、すべてのフィールドを明示的に公開する必要があります。Prisma モデルからすべてを公開するオプションはありません。

したがって、Profile の新しい定義では、すべてのフィールドを明示的にリストする必要があります。

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

t.model は、name 属性を参照し、Prisma スキーマ内のモデルと照合することに注意してください。この場合、Profile モデルと照合されます。したがって、t.modelProfile モデルのフィールドの名前が付けられた関数を公開します。

3.4. Category 型の移行

以前の nexus-prisma パッケージを使用した型の定義

サンプルアプリでは、Category 型は次のように定義されています。

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

prismaFields のアスタリスクは、すべて の Prisma ORM フィールドが公開されていることを意味します。

@nexus/schemanexus-plugin-prisma の最新バージョンを使用した型の定義

@nexus/schema の最新バージョンでは、すべてのフィールドを明示的に公開する必要があります。Prisma モデルからすべてを公開するオプションはありません。

したがって、Category の新しい定義では、すべてのフィールドを明示的にリストする必要があります。

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

t.model は、name 属性を参照し、Prisma スキーマ内のモデルと照合することに注意してください。この場合、Category モデルと照合されます。したがって、t.modelCategory モデルのフィールドの名前が付けられた関数を公開します。

4. GraphQL 操作の移行

次のステップとして、「以前の」GraphQL API から新しい GraphQL API に、すべての GraphQL クエリミューテーション の移行を開始できます。

このガイドでは、次のサンプル GraphQL 操作を使用します。

input UserUniqueInput {
id: String
email: String
}

type Query {
posts(searchString: String): [Post!]!
user(userUniqueInput: UserUniqueInput!): User
users(where: UserWhereInput, orderBy: Enumerable<UserOrderByInput>, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
}

type Mutation {
createUser(data: UserCreateInput!): User!
createDraft(title: String!, content: String, authorId: ID!): Post
updateBio(userUniqueInput: UserUniqueInput!, bio: String!): User
addPostToCategories(postId: String!, categoryIds: [String!]!): Post
}

4.1. GraphQL クエリの移行

このセクションでは、以前のバージョンの nexusnexus-prisma から最新バージョンの @nexus/schemanexus-plugin-prisma に、すべての GraphQL クエリ を移行します。

4.1.1. users クエリの移行

サンプル API では、サンプル GraphQL スキーマの users クエリは次のように実装されています。

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

新しい Nexus で同じ動作を実現するには、t.crudusers 関数を呼び出す必要があります。

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

crud プロパティは、nexus-plugin-prisma によって t に追加されることを思い出してください(t.model と同じメカニズムを使用)。

4.1.2. posts(searchString: String): [Post!]! クエリの移行

サンプル API では、posts クエリは次のように実装されています。

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

このクエリで更新する必要があるのは、新しい Prisma Client API が Prisma 1 で使用されているものとは少し異なるため、Prisma ORM の呼び出しだけです。

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

db オブジェクトは、nexus-plugin-prisma によって自動的に context にアタッチされることに注意してください。これは PrismaClient のインスタンスを表し、リゾルバー内でデータベースにクエリを送信できるようにします。

4.1.3. user(uniqueInput: UserUniqueInput): User クエリの移行

サンプル API では、user クエリは次のように実装されています。

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

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

新しい Prisma Client API が Prisma 1 で使用されているものとは少し異なるため、prisma インスタンスへの呼び出しを調整する必要があります。

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

4.2. GraphQL ミューテーションの移行

このセクションでは、サンプルスキーマからの GraphQL ミューテーションを、最新バージョンの @nexus/schemanexus-plugin-prisma に移行します。

4.2.1. createUser ミューテーションの移行

サンプル API では、サンプル GraphQL スキーマの createUser ミューテーションは次のように実装されています。

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

最新バージョンの @nexus/schemanexus-plugin-prisma で同じ動作を実現するには、t.crudcreateOneUser 関数を呼び出し、GraphQL スキーマのフィールドの名前を createUser に変更するために alias を渡す必要があります(そうしないと、使用されている関数にちなんで createOneUser という名前になります)。

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

crud プロパティは、nexus-plugin-prisma によって t に追加されることを思い出してください(t.model と同じメカニズムを使用)。

4.2.2. createDraft(title: String!, content: String, authorId: String!): Post! クエリの移行

サンプルアプリでは、createDraft ミューテーションは次のように実装されています。

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

新しい Prisma Client API が Prisma 1 で使用されているものとは少し異なるため、prisma インスタンスへの呼び出しを調整する必要があります。

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

4.2.3. updateBio(bio: String, userUniqueInput: UserUniqueInput!): User ミューテーションの移行

サンプル API では、updateBio ミューテーションは次のように定義および実装されています。

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

新しい Prisma Client API が Prisma 1 で使用されているものとは少し異なるため、prisma インスタンスへの呼び出しを調整する必要があります。

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

4.2.4. addPostToCategories(postId: String!, categoryIds: [String!]!): Post ミューテーションの移行

サンプル API では、addPostToCategories ミューテーションは次のように定義および実装されています。

mutationType({
definition(t) {
t.field('addPostToCategories', {
type: 'Post',
args: {
postId: stringArg({ nullable: false }),
categoryIds: stringArg({
list: true,
nullable: false,
}),
},
resolve: (_, args, context) => {
const ids = args.categoryIds.map((id) => ({ id }))
return context.prisma.updatePost({
where: {
id: args.postId,
},
data: {
categories: { connect: ids },
},
})
},
})
},
})

新しい Prisma Client API が Prisma 1 で使用されているものとは少し異なるため、prisma インスタンスへの呼び出しを調整する必要があります。

const Mutation = mutationType({
definition(t) {
t.field('addPostToCategories', {
type: 'Post',
args: {
postId: stringArg({ nullable: false }),
categoryIds: stringArg({
list: true,
nullable: false,
}),
},
resolve: (_, args, context) => {
const ids = args.categoryIds.map((id) => ({ id }))
return context.prisma.post.update({
where: {
id: args.postId,
},
data: {
categories: { connect: ids },
},
})
},
})
},
})

5. クリーンアップ

5.1. npm 依存関係のクリーンアップ

まだ行っていない場合は、Prisma 1 のセットアップに関連する依存関係をアンインストールできます。

npm uninstall prisma1 prisma-client-lib

5.2. 未使用ファイルの削除

次に、Prisma 1 セットアップのファイルを削除します。

rm -rf src/generated
rm -rf prisma1

5.3. Prisma ORM サーバーの停止

最後に、Prisma ORM サーバーの実行を停止できます。