👻

prismaのseedを複数ファイルに分割する

2022/02/24に公開

はじめに

Prismaでは、下記のようにpackage.jsonにseedコマンドを設定することが出来ます。

package.json
"prisma": {
  "seed": "ts-node prisma/seed.ts"
}

seedの実行はprisma db seedで手動で実行することができますが、prisma migrate devprisma migrate resetを実行した時にも自動で実行されます。

https://www.prisma.io/docs/guides/database/seed-database

多対多のリレーションを持つテーブルを作成

公式ドキュメントのprisma.schemaの定義をそのままコピーして、seedでデータを投入するテーブルを作成します。

prisma.schema
model Post {
  id         Int                 @id @default(autoincrement())
  title      String
  categories CategoriesOnPosts[]
}

model Category {
  id    Int                 @id @default(autoincrement())
  name  String
  posts CategoriesOnPosts[]
}

model CategoriesOnPosts {
  post       Post     @relation(fields: [postId], references: [id])
  postId     Int // relation scalar field (used in the `@relation` attribute above)
  category   Category @relation(fields: [categoryId], references: [id])
  categoryId Int // relation scalar field (used in the `@relation` attribute above)
  assignedAt DateTime @default(now())
  assignedBy String

  @@id([postId, categoryId])
}

https://www.prisma.io/docs/concepts/components/prisma-schema/relations/many-to-many-relations

ディレクトリ構造

分割したseedを入れるためのフォルダとseedファイルを作成します。
start.tsがseedの起点となります。

tree prisma               
prisma
├── migrations
├── schema.prisma
└── seed
    ├── category.ts
    ├── post.ts
    └── start.ts
    └── categories-on-posts.ts

package.jsonの設定

package.jsonにprismaのseedの設定を加えます。

package.json
  "prisma": {
    "seed": "ts-node ./prisma/seed/start.ts"
  }

start.ts

start.tsは下記の通りです。
ESMで関数をimportしてawaitで非同期処理を順番に実行しています。

start.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
import { category } from './category';
import { post } from './post';
import { categoriesOnPosts } from './categories-on-posts';

async function main() {
  await prisma.categoriesOnPosts.deleteMany();
  await prisma.post.deleteMany();
  await prisma.category.deleteMany();
  await post();
  await category();
  await categoriesOnPosts();
}

main()
  .catch((e) => console.error(e))
  .finally(async () => {
    await prisma.$disconnect();
  });

seed

特に難しいところはありません。

post.ts

post.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export const post = async () => {
 await prisma.post.createMany({
   data: Array(60)
     .fill(0)
     .map((v, i) => ({
       title: 'post' + i.toString(),
     })),
 });
};

category.ts

post.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export const category = async () => {
 await prisma.category.createMany({
   data: Array(60)
     .fill(0)
     .map((v, i) => ({
       name: 'category' + i.toString(),
     })),
 });
};

categories-on-posts.ts

categories-on-posts.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

export const categoriesOnPosts = async () => {
 const posts = await prisma.post.findMany();
 const categories = await prisma.category.findMany();
 
 for (const post of posts) {
   for (const category of categories) {
     await prisma.categoriesOnPosts.create({
       data: {
         postId: post.id,
         categoryId: category.id,
         assignedBy: 'author',
       },
     });
   }
 }
};

まとめ

テーブル数が多くなってくると、1ファイルにseedを書くのが辛くなってくると思います。
また、1度の関数の実行で二つ以上のテーブルにデータを投入することもできますが、1ファイルで1つのテーブルのみにデータを投入するようにする事で、どのファイルがどのテーブルにデータを投入しているのか分かりやすくなり、seedを管理しやすくなります。

Discussion