古い Nexus から新しい Nexus へ
概要
このガイドは、現在 非推奨 バージョンの nexus-plugin-prisma
を使用しているため、完全に最新ではありません。これはまだ機能しますが、新しい nexus-prisma
ライブラリまたは Pothos のような代替のコードファースト GraphQL ライブラリを今後使用することをお勧めします。ご不明な点がございましたら、Discord でお気軽にご質問ください。
このアップグレードガイドでは、GraphQL サーバーを実装するために、Prisma 1 と nexus
(< v0.12.0) または @nexus/schema
と nexus-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 プラグインの使用方法も最新バージョンで変更されています。
そのような構成の例を次に示します。
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
オブジェクトの型を指定していた場合は、次のように型を調整する必要があります。
import { Prisma } from './generated/prisma-client'
import { PrismaClient } from '@prisma/client'
export interface Context {
prisma: Prisma
prisma: PrismaClient
}
3. GraphQL 型の移行
@nexus/schema
と nexus-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/schema
と nexus-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.model
は User
モデルのフィールドの名前が付けられた関数を公開します。
この時点で、リレーションフィールド posts
と profile
でエラーが発生する可能性があります。例:
//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/schema
と nexus-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.model
は Post
モデルのフィールドの名前が付けられた関数を公開します。
3.3. Profile
型の移行
以前の nexus-prisma
パッケージを使用した型の定義
サンプルアプリでは、Profile
型は次のように定義されています。
const Profile = prismaObjectType({
name: 'Profile',
definition(t) {
t.prismaFields(['*'])
},
})
prismaFields
のアスタリスクは、すべて の Prisma フィールドが公開されていることを意味します。
@nexus/schema
と nexus-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.model
は Profile
モデルのフィールドの名前が付けられた関数を公開します。
3.4. Category
型の移行
以前の nexus-prisma
パッケージを使用した型の定義
サンプルアプリでは、Category
型は次のように定義されています。
const Category = prismaObjectType({
name: 'Category',
definition(t) {
t.prismaFields(['*'])
},
})
prismaFields
のアスタリスクは、すべて の Prisma ORM フィールドが公開されていることを意味します。
@nexus/schema
と nexus-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.model
は Category
モデルのフィールドの名前が付けられた関数を公開します。
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 クエリの移行
このセクションでは、以前のバージョンの nexus
と nexus-prisma
から最新バージョンの @nexus/schema
と nexus-plugin-prisma
に、すべての GraphQL クエリ を移行します。
4.1.1. users
クエリの移行
サンプル API では、サンプル GraphQL スキーマの users
クエリは次のように実装されています。
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.prismaFields(['users'])
},
})
新しい Nexus で同じ動作を実現するには、t.crud
で users
関数を呼び出す必要があります。
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/schema
と nexus-plugin-prisma
に移行します。
4.2.1. createUser
ミューテーションの移行
サンプル API では、サンプル GraphQL スキーマの createUser
ミューテーションは次のように実装されています。
const Mutation = prismaObjectType({
name: 'Mutation',
definition(t) {
t.prismaFields(['createUser'])
},
})
最新バージョンの @nexus/schema
と nexus-plugin-prisma
で同じ動作を実現するには、t.crud
で createOneUser
関数を呼び出し、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 サーバーの実行を停止できます。