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

旧Nexusから新Nexusへ

概要

情報

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

このアップグレードガイドでは、Prisma 1をベースとし、nexus (v0.12.0未満) または@nexus/schemanexus-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プラグインの使用方法も最新バージョンでは変更されています。

以下に、そのような設定の例を示します。

./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. 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/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から新しい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の*クエリ*を、以前のバージョンのnexusnexus-prismaから、最新バージョンの@nexus/schemanexus-plugin-prismaに移行します。

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 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/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サーバーの実行を停止できます。

© . All rights reserved.