旧Nexusから新Nexusへ
概要
このガイドは、現在非推奨バージョンのnexus-plugin-prisma
を使用しているため、完全に最新のものではありません。これはまだ機能しますが、今後は新しいnexus-prisma
ライブラリ、またはPothosのような代替のコードファーストGraphQLライブラリを使用することをお勧めします。ご質問がある場合は、お気軽にDiscordで共有してください。
このアップグレードガイドでは、Prisma 1をベースとし、nexus
(v0.12.0未満) または@nexus/schema
とnexus-prisma
(v4.0.0未満) を使用してGraphQLサーバーを実装するプロジェクトをアップグレードする方法を説明します。
コードは@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
次に、Nexus用のPrisma ORMプラグインをインストールします。これにより、Prisma ORMモデルをGraphQL APIに公開できるようになります(これは以前の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. User
型の移行
以前の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から新しい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クエリの移行
このセクションでは、GraphQLの*クエリ*を、以前のバージョンのnexus
とnexus-prisma
から、最新バージョンの@nexus/schema
とnexus-plugin-prisma
に移行します。
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 ORMへの呼び出しのみです。新しいPrisma Client APIは、Prisma 1で使用されていたものと少し異なります。
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サーバーの実行を停止できます。