Sequelizeからの移行
このガイドでは、SequelizeからPrisma ORMへの移行方法について説明します。移行手順を示すために、Sequelize Expressの例を拡張したものをサンプルプロジェクトとして使用します。このガイドで使用した例は、GitHubにあります。
この移行ガイドでは、例としてPostgreSQLデータベースを使用しますが、Prismaでサポートされている他のリレーショナルデータベースにも同様に適用できます。
Prisma ORMとSequelizeの比較については、Prisma ORM vs Sequelizeのページをご覧ください。
移行プロセスの概要
SequelizeからPrisma ORMへの移行手順は、どのようなアプリケーションやAPIレイヤーを構築しているかに関わらず、常に同じであることに注意してください。
- Prisma CLIをインストールする
- データベースをイントロスペクトする
- ベースライン移行を作成する
- Prisma Clientをインストールする
- SequelizeクエリをPrisma Clientで段階的に置き換える
これらの手順は、REST API(例えば、Express、koa、NestJSなど)、GraphQL API(例えば、Apollo Server、TypeGraphQL、Nexusなど)、またはデータベースアクセスにSequelizeを使用するその他の種類のアプリケーションを構築している場合でも適用されます。
Prisma ORMは、段階的な導入に非常に適しています。つまり、プロジェクト全体をSequelizeからPrisma ORMに一度に移行する必要はなく、データベースクエリをSequelizeからPrisma ORMに段階的に移動することができます。
サンプルプロジェクトの概要
このガイドでは、Expressで構築されたREST APIをサンプルプロジェクトとして使用し、Prisma ORMに移行します。これには4つのモデル/エンティティがあります。
- User.js
- Post.js
- Profile.js
- Category.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
})
User.associate = (models) => {
User.hasMany(models.Post, {
foreignKey: 'authorId',
as: 'posts',
})
User.hasOne(models.Profile, {
onDelete: 'CASCADE',
foreignKey: 'userId',
})
}
return User
}
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: {
type: DataTypes.STRING,
allowNull: false,
},
content: {
type: DataTypes.STRING,
},
published: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
})
Post.associate = (models) => {
Post.belongsTo(models.User, {
foreignKey: 'authorId',
as: 'author',
})
Post.belongsToMany(models.Category, {
through: 'PostCategories',
as: 'categories',
})
}
return Post
}
module.exports = (sequelize, DataTypes) => {
const Profile = sequelize.define('Profile', {
bio: {
type: DataTypes.STRING,
allowNull: false,
},
})
Profile.associate = (models) => {
Profile.belongsTo(models.User, {
foreignKey: 'userId',
as: 'user',
})
}
return Profile
}
module.exports = (sequelize, DataTypes) => {
const Category = sequelize.define('Category', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
})
Category.associate = (models) => {
Category.belongsToMany(models.Post, {
through: 'PostCategories',
as: 'posts',
})
}
return Category
}
モデルには次の関係があります。
- 1対1:
User
↔Profile
- 1対多:
User
↔Post
- 多対多:
Post
↔Category
対応するテーブルは、生成されたSequelizeのマイグレーションを使用して作成されています。
このガイドでは、ルートハンドラーはsrc/controllers
ディレクトリにあります。モデルはsrc/models
ディレクトリにあります。そこから、中央のsrc/routes.js
ファイルにプルされ、src/index.js
で必要なルートを設定するために使用されます。
└── blog-sequelize
├── package.json
└──src
├── controllers
│ ├── post.js
│ └── user.js
├── models
│ ├── Category.js
│ ├── Post.js
│ ├── Profile.js
│ └── User.js
├── index.js
└── routes.js
ステップ1. Prisma CLIをインストールする
Prisma ORMを採用するための最初のステップは、プロジェクトにPrisma CLIをインストールすることです。
npm install prisma --save-dev
ステップ2. データベースをイントロスペクトする
2.1. Prisma ORMを設定する
データベースをイントロスペクトする前に、Prismaスキーマを設定し、Prisma ORMをデータベースに接続する必要があります。ターミナルで次のコマンドを実行して、基本的なPrismaスキーマファイルを作成します。
npx prisma init
このコマンドは、次のファイルを含むprisma
という新しいディレクトリを作成しました。
schema.prisma
: データベース接続とモデルを指定するPrismaスキーマ.env
: 環境変数としてデータベース接続URLを設定するためのdotenv
現在のPrismaスキーマは次のようになっています。
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
VS Codeを使用している場合は、必ずPrisma VS Code拡張機能をインストールして、構文の強調表示、フォーマット、オートコンプリート、その他の便利な機能を使用してください。
2.2. データベースを接続する
PostgreSQLを使用していない場合は、datasource
ブロックのprovider
フィールドを現在使用しているデータベースに合わせて調整する必要があります。
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlserver"
url = env("DATABASE_URL")
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
完了したら、データベース接続URLを.env
ファイルで設定できます。Sequelizeからのデータベース接続がPrisma ORMで使用される接続URL形式にどのようにマッピングされるかは次のとおりです。
- PostgreSQL
- MySQL
- Microsoft SQL Server
- SQLite
src/models/index.js
に次のデータベース接続詳細があると仮定します。
const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', {
host: 'localhost',
dialect: 'postgres',
})
Prisma ORMでは、対応する接続URLは次のようになります。
DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-sequelize"
オプションで、接続URLにschema
引数を追加してPostgreSQLのスキーマを設定できることに注意してください。
DATABASE_URL="postgresql://alice:myPassword42@localhost:5432/blog-sequelize?schema=myschema"
指定しない場合、public
という名前のデフォルトスキーマが使用されます。
src/models/index.js
に次のデータベース接続詳細があると仮定します。
const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', {
host: 'localhost',
dialect: 'postgres',
})
Prismaでは、対応する接続URLは次のようになります。
DATABASE_URL="mysql://alice:myPassword42@localhost:3306/blog-sequelize"
src/models/index.js
に次のデータベース接続詳細があると仮定します。
const sequelize = new Sequelize('blog-sequelize', 'alice', 'myPassword42', {
host: 'localhost',
dialect: 'mssql',
})
Prismaでは、対応する接続URLは次のようになります。
DATABASE_URL="sqlserver://localhost:1433;database=blog-sequelize;user=alice;password=myPassword42;trustServerCertificate=true"
src/models/index.js
に次のデータベース接続詳細があると仮定します。
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: '../../blog-sequelize.sqlite',
})
Prismaでは、対応する接続URLは次のようになります。
DATABASE_URL="file:./blog-sequelize.db"
2.3. Prisma ORMを使用してデータベースをイントロスペクトする
接続URLを設定したら、データベースをイントロスペクトして、Prismaモデルを生成できます。
npx prisma db pull
これにより、次のPrismaモデルが作成されます。
model Categories {
id Int @id @default(autoincrement())
name String
createdAt DateTime
updatedAt DateTime
PostCategories PostCategories[]
}
model PostCategories {
createdAt DateTime
updatedAt DateTime
CategoryId Int
PostId Int
Categories Categories @relation(fields: [CategoryId], references: [id])
Posts Posts @relation(fields: [PostId], references: [id])
@@id([CategoryId, PostId])
}
model Posts {
id Int @id @default(autoincrement())
title String
content String?
published Boolean? @default(false)
createdAt DateTime
updatedAt DateTime
authorId Int?
Users Users? @relation(fields: [authorId], references: [id])
PostCategories PostCategories[]
}
model Profiles {
id Int @id @default(autoincrement())
bio String
createdAt DateTime
updatedAt DateTime
userId Int? @unique
Users Users? @relation(fields: [userId], references: [id])
}
model SequelizeMeta {
name String @id
}
model Users {
id Int @id @default(autoincrement())
name String?
email String @unique
createdAt DateTime
updatedAt DateTime
Posts Posts[]
Profiles Profiles?
}
2.4. ベースライン移行を作成する
Prisma Migrateを使用してデータベーススキーマを進化させ続けるには、データベースをベースライン化する必要があります。
まず、migrations
ディレクトリを作成し、その中に移行に使用する適切な名前のディレクトリを追加します。この例では、移行名として0_init
を使用します。
mkdir -p prisma/migrations/0_init
次に、prisma migrate diff
を使用して移行ファイルを生成します。次の引数を使用します。
--from-empty
: 移行元のデータモデルが空であると仮定します。--to-schema-datamodel
:datasource
ブロックのURLを使用した現在のデータベース状態。--script
: SQLスクリプトを出力します。
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
生成された移行を確認して、すべてが正しいことを確認します。
次に、--applied
引数を使用してprisma migrate resolve
を使用し、移行が適用済みとしてマークします。
npx prisma migrate resolve --applied 0_init
このコマンドは、_prisma_migrations
テーブルに追加することで、0_init
を適用済みとしてマークします。
これで、現在のデータベーススキーマのベースラインができました。データベーススキーマをさらに変更するには、Prismaスキーマを更新し、prisma migrate dev
を使用して変更をデータベースに適用できます。
2.5. createdAt
および updatedAt
フィールドの調整
生成された Prisma モデルは、データベーステーブルを表しており、データベースにクエリを送信するためのプログラムによる Prisma Client API の基盤となります。モデル内の createdAt
および updatedAt
フィールドを調整します。Sequelize は、データベースにテーブルを作成する際に createdAt
に DEFAULT
制約を追加しません。したがって、createdAt
および updatedAt
列にそれぞれ @default(now())
および @updatedAt
属性を追加します。Prisma ORM がどのようにこれを行うかについて詳しくは、@default(now())
および @updatedAt
をご覧ください。更新されたスキーマは次のようになります。
model Categories {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
PostCategories PostCategories[]
}
model PostCategories {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
CategoryId Int
PostId Int
Categories Categories @relation(fields: [CategoryId], references: [id])
Posts Posts @relation(fields: [PostId], references: [id])
@@id([CategoryId, PostId])
}
model Posts {
id Int @id @default(autoincrement())
title String
content String?
published Boolean? @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int?
Users Users? @relation(fields: [authorId], references: [id])
PostCategories PostCategories[]
}
model Profiles {
id Int @id @default(autoincrement())
bio String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId Int? @unique
Users Users? @relation(fields: [userId], references: [id])
}
model SequelizeMeta {
name String @id
}
model Users {
id Int @id @default(autoincrement())
name String?
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Posts Posts[]
Profiles Profiles?
}
2.6. Prisma スキーマの調整 (オプション)
イントロスペクションによって生成されたモデルは、現在、データベーステーブルに正確にマッピングされています。このセクションでは、Prisma ORM の命名規則に従うように、Prisma モデルの名前を調整する方法を学びます。
これらの調整はすべて完全にオプションであり、現時点で何も調整したくない場合は、すでに次のステップに進んでも構いません。後でいつでも戻って調整を行うことができます。
現在の Prisma モデルのスネークケース表記とは対照的に、Prisma ORM の命名規則は次のとおりです。
- モデル名には PascalCase
- フィールド名には camelCase
Prisma モデルとフィールド名を、@@map
および @map
を使用して、基になるデータベースの既存のテーブル名および列名にマッピングすることにより、名前を調整できます。
また、後でデータベースにクエリを送信するために使用する Prisma Client API を最適化するために、リレーションフィールドの名前を変更することもできます。たとえば、Posts
モデル名を単数形の Post
に変更していますが、user
モデルの posts
フィールドはリストであるため、複数形であることを示すために posts
という名前を維持することが理にかなっています。
Sequelize は、ライブラリによって内部的に使用される SequelizeMeta
モデルを生成しますが、これは不要です。したがって、スキーマから手動で削除します。
これらの点を考慮した、調整された Prisma スキーマのバージョンを以下に示します。
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Category {
id Int @id @default(autoincrement())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
postCategories PostToCategories[]
@@map("Categories")
}
model PostToCategories {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
categoryId Int
postId Int
category Category @relation(fields: [categoryId], references: [id])
post Post @relation(fields: [postId], references: [id])
@@id([categoryId, postId])
@@map("PostCategories")
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean? @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
authorId Int?
author User? @relation(fields: [authorId], references: [id])
postToCategories PostToCategories[]
@@map("Posts")
}
model Profile {
id Int @id @default(autoincrement())
bio String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId Int? @unique
user User? @relation(fields: [userId], references: [id])
@@map("Profiles")
}
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
profile Profile?
@@map("Users")
}
ステップ 3. Prisma Client のインストール
次のステップとして、プロジェクトに Prisma Client をインストールして、現在 Sequelize で作成されているプロジェクト内のデータベースクエリの置き換えを開始できます。
npm install @prisma/client
ステップ 4. Sequelize クエリを Prisma Client に置き換える
このセクションでは、サンプル REST API プロジェクトのサンプルルートに基づいて、Sequelize から Prisma Client に移行するいくつかのサンプルクエリを示します。Prisma Client API が Sequelize とどのように異なるかについて包括的に知りたい場合は、API 比較ページを参照してください。
まず、さまざまなルートハンドラからデータベースクエリを送信するために使用する PrismaClient
インスタンスを設定します。src
ディレクトリに prisma.js
という名前の新しいファイルを作成します。
touch src/prisma.js
次に、PrismaClient
をインスタンス化し、後でルートハンドラで使用できるようにファイルからエクスポートします。
const { PrismaClient } = require('@prisma/client')
const prisma = new PrismaClient()
module.exports = prisma
コントローラファイルでのインポートは次のようになります。
const { Post, User, Category } = require('../models')
const { Op } = require('sequelize')
const { User } = require('../models')
Sequelize から Prisma に移行するにつれて、コントローラのインポートを更新します。
const prisma = require('../prisma')
const prisma = require('../prisma')
4.1. GET
リクエストでのクエリの置換
REST API には、GET
リクエストを受け入れる 4 つのルートがあります。
/feed
: 公開されているすべての投稿を返します/filterPosts?searchString=SEARCH_STRING
: 返された投稿をSEARCH_STRING
でフィルタリングします/post/:postId
: 特定の投稿を返します/authors
: 作成者の一覧を返します
これらのリクエストを実装するルートハンドラを見ていきましょう。
/feed
現在、/feed
ハンドラは次のように実装されています。
const feed = async (req, res) => {
try {
const feed = await Post.findAll({
where: { published: true },
include: ['author', 'categories'],
})
return res.json(feed)
} catch (error) {
return res.status(500).json(error)
}
}
返される各 Post
オブジェクトには、関連付けられている author
および category
へのリレーションが含まれていることに注意してください。Sequelize では、リレーションを含めることはタイプセーフではありません。たとえば、取得されるリレーションにタイプミスがあった場合、データベースクエリは実行時にのみ失敗します。JavaScript コンパイラはここでは安全を提供しません。
Prisma Client を使用して同じルートを実装する方法を以下に示します。
const feed = async (req, res) => {
try {
const feed = await prisma.post.findMany({
where: { published: true },
include: { author: true, postToCategories: true },
})
return res.json(feed)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma Client が author
リレーションを含める方法は完全にタイプセーフであることに注意してください。Post
モデルに存在しないリレーションを含めようとすると、JavaScript コンパイラがエラーをスローします。
/filterPosts?searchString=SEARCH_STRING
現在、/filterPosts
ハンドラは次のように実装されています。
const filterPosts = async (req, res) => {
const { searchString } = req.query
try {
const filteredPosts = await Post.findAll({
where: {
[Op.or]: [
{
title: {
[Op.like]: `%${searchString}%`,
},
},
{
content: {
[Op.like]: `%${searchString}%`,
},
},
],
},
include: 'author',
})
res.json(filteredPosts)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const filterPosts = async (req, res) => {
const { searchString } = req.query
try {
const filteredPosts = prisma.post.findMany({
where: {
OR: [
{
title: { contains: searchString },
},
{
content: { contains: searchString },
},
],
},
})
res.json(filteredPosts)
} catch (error) {
return res.status(500).json(error)
}
}
Sequelize は、データのクエリ時に使用する 演算子シンボル - Op
- を提供していることに注意してください。一方、Prisma ORM は、複数の where
条件を暗黙的な AND
演算子と組み合わせているため、この場合、Prisma Client クエリは OR
を明示的に行う必要があります。
/post/:postId
現在、/post/:postId
ハンドラは次のように実装されています。
const getPostById = async (req, res) => {
const { postId } = req.params
try {
const post = await Post.findOne({
where: { id: postId },
include: 'author',
})
return res.json(post)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const getPostById = async (req, res) => {
const { postId } = req.params
try {
const post = await prisma.post.findUnique({
where: { id: Number(postId) },
include: { author: true },
})
return res.json(post)
} catch (error) {
return res.status(500).json(error)
}
}
4.2. POST
リクエストでのクエリの置換
REST API には、POST
リクエストを受け入れる 3 つのルートがあります。
/user
: 新しいUser
レコードを作成します/post
: 新しいUser
レコードを作成します/user/:userId/profile
: 指定された ID を持つUser
レコードの新しいProfile
レコードを作成します
/user
現在、/user
ハンドラは次のように実装されています。
const createUser = async (req, res) => {
const { name, email } = req.body
try {
const user = await User.create({
name,
email,
})
return res.json(user)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const createUser = async (req, res) => {
const { name, email } = req.body
try {
const user = await prisma.user.create({
data: {
name,
email,
},
})
return res.json(user)
} catch (error) {
return res.status(500).json(error)
}
}
/post
現在、/post
ハンドラは次のように実装されています。
const createDraft = async (req, res) => {
const { title, content, authorEmail } = req.body
try {
const user = await User.findOne({ email: authorEmail })
const draft = await Post.create({
title,
content,
authorId: user.id,
})
res.json(draft)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const createDraft = async (req, res) => {
const { title, content, authorEmail } = req.body
try {
const draft = await prisma.post.create({
data: {
title,
content,
author: {
connect: { email: authorEmail },
},
},
})
res.json(draft)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma Client のネストされた書き込みでは、最初に email
で User
レコードを取得する必要がある最初のクエリが保存されることに注意してください。これは、Prisma Client を使用すると、一意のプロパティを使用してリレーションのレコードを接続できるためです。
/user/:userId/profile
現在、/user/:userId/profile
ハンドラは次のように実装されています。
const setUserBio = async (req, res) => {
const { userId } = req.params
const { bio } = req.body
try {
const user = await User.findOne({
where: {
id: Number(userId),
},
})
const updatedUser = await user.createProfile({ bio })
return res.json(updatedUser)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const setUserBio = async (req, res) => {
const { userId } = req.params
const { bio } = req.body
try {
const user = await prisma.user.update({
where: { id: Number(userId) },
data: {
profile: {
create: { bio },
},
},
})
return res.json(user)
} catch (error) {
return res.status(500).json(error)
}
}
4.3. PUT
リクエストでのクエリの置換
REST API には、PUT
リクエストを受け入れる 1 つのルートがあります。
/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID
:POST_ID
を持つ投稿をCATEGORY_ID
を持つカテゴリに追加します
これらのリクエストを実装するルートハンドラを見ていきましょう。
/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID
現在、/addPostToCategory?postId=POST_ID&categoryId=CATEGORY_ID
ハンドラは次のように実装されています。
const addPostToCategory = async (req, res) => {
const { postId, categoryId } = req.query
try {
const post = await Post.findOne({
where: { id: postId },
})
const category = await Category.findOne({
where: { id: categoryId },
})
const updatedPost = await post.addCategory(category)
return res.json(updatedPost)
} catch (error) {
return res.status(500).json(error)
}
}
Prisma ORM では、ルートは次のように実装されます。
const addPostToCategory = async (req, res) => {
const { postId, categoryId } = req.query
try {
const post = await prisma.post.update({
data: {
postToCategories: {
create: {
categories: {
connect: { id: Number(categoryId) },
},
},
},
},
where: {
id: Number(postId),
},
})
return res.json(post)
} catch (error) {
return res.status(500).json(error)
}
}
この Prisma Client は、リレーションを暗黙的な多対多リレーションとしてモデル化することで、冗長性を減らすことができます。その場合、クエリは次のようになります。
const post = await prisma.post.update({
data: {
category: {
connect: { id: categoryId },
},
},
where: { id: postId },
})
その他
主キー列
デフォルトでは、Sequelize は primaryKey
を定義し、定義されていない場合はデフォルトで id
を使用しました。これはオプションです。独自の主キーを設定する場合は、primaryKey: true
を使用し、選択したフィールドで優先するデータ型を定義できます。
// changing the primary key column
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
postId: {
type: DataTypes.INTEGER,
primaryKey: true,
},
})
return Post
}
// changing the id DataType
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
id: {
type: DataTypes.UUID, // alternative: DataTypes.STRING
primaryKey: true,
},
})
return Post
}
テーブル名の推論
Sequelize は、モデル名からテーブル名を推測します。テーブル名が指定されていない場合、Sequelize はモデル名を自動的に複数形にし、inflectionというライブラリを使用してテーブル名として使用します。一方、Prisma ORM は、データのモデリングで、モデル名をデータベース内のテーブル名にマッピングします。Sequelize でこのデフォルトの動作を変更する場合は、テーブル名をモデル名と等しくなるように強制するか、テーブル名を直接指定できます。
// enforcing table name to be equal to model name
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
'Post',
{
// ... attributes
},
{
freezeTableName: true,
}
)
return Post
}
// providing the table name directly
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
'Post',
{
// ... attributes
},
{
tableName: 'Post',
}
)
return Post
}
タイムスタンプ
Sequelize は、デフォルトで、データ型 DataTypes.DATE
を使用して、すべてのモデルにフィールド createdAt
および updatedAt
を自動的に追加します。timestamps: false
オプションを使用すると、モデルでこれを無効にできます。
sequelize.define(
'User',
{
// ... (attributes)
},
{
timestamps: false,
}
)
Prisma ORM では、これらのフィールドをモデルで柔軟に定義できます。createdAt
および updatedAt
フィールドをモデルで明示的に定義することにより追加します。モデルに createdAt
フィールドを設定するには、列に default(now())
属性を追加します。updatedAt
列を設定するには、列に @updatedAt
属性を追加してモデルを更新します。
model User {
id Int @id @default(autoincrement())
name String?
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
暗黙的な多対多の関係
SequelizeのbelongsToMany()
関連付けメソッドと同様に、Prisma ORMでは多対多の関係を暗黙的にモデル化できます。つまり、関係テーブル(JOINテーブルとも呼ばれる)をスキーマで明示的に管理する必要がない多対多の関係です。以下はSequelizeの例です。
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: {
type: DataTypes.STRING,
allowNull: false,
},
content: {
type: DataTypes.STRING,
},
published: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
})
Post.associate = (models) => {
Post.belongsTo(models.User, {
foreignKey: 'authorId',
as: 'author',
})
Post.belongsToMany(models.Category, {
through: 'PostCategories',
as: 'categories',
})
}
return Post
}
module.exports = (sequelize, DataTypes) => {
const Category = sequelize.define('Category', {
name: {
type: DataTypes.STRING,
allowNull: false,
},
})
Category.associate = (models) => {
Category.belongsToMany(models.Post, {
through: 'PostCategories',
as: 'posts',
})
}
return Category
}
アプリケーションを起動すると、Sequelizeはこれらのモデルに基づいてテーブルを作成します。
Executing (default): CREATE TABLE IF NOT EXISTS "PostCategories"
("createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
"CategoryId" INTEGER REFERENCES "Categories" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
"PostId" INTEGER REFERENCES "Posts" ("id") ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY ("CategoryId","PostId"));
Prisma ORMでデータベースをイントロスペクトすると、Prismaスキーマで次の結果が得られます(一部のリレーションフィールド名は、イントロスペクションからの生のバージョンと比較して、よりわかりやすく調整されていることに注意してください)。
model Categories {
id Int @id @default(autoincrement())
name String
createdAt DateTime
updatedAt DateTime
PostCategories PostCategories[]
@@map("category")
}
model PostCategories {
createdAt DateTime
updatedAt DateTime
CategoryId Int
PostId Int
Categories Categories @relation(fields: [CategoryId], references: [id])
Posts Posts @relation(fields: [PostId], references: [id])
@@id([CategoryId, PostId])
@@map("PostCategories")
}
model Posts {
id Int @id @default(autoincrement())
title String
content String?
published Boolean? @default(false)
createdAt DateTime
updatedAt DateTime
authorId Int?
Users Users? @relation(fields: [authorId], references: [id])
PostCategories PostCategories[]
@@map("post")
}
このPrismaスキーマでは、多対多の関係は関係テーブルPostCategories
を介して明示的にモデル化されています。
Prismaの関係テーブルの規約に従うことで、関係は次のようになります。
model Categories {
id Int @id @default(autoincrement())
name String
posts Posts[]
@@map("category")
}
model Posts {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int?
author User? @relation(fields: [authorId], references: [id])
categories Categories[]
@@map("post")
}
これにより、この関係のレコードを変更するためのPrisma Client APIがより人間工学的で簡潔になります。なぜなら、PostCategories
モデルを最初にたどる必要はなく、Post
からCategory
への直接パス(およびその逆)を持つことができるからです。