PR
GraphQLNexusPostgreSQLPrismaReact Native npx prisma db seednpx prisma studioPrisma Studio

Prisma の seed でデータを PostgreSQL に一括投入する方法

apollo-server-nexus-02 GraphQL

関連記事は⬇︎⬇︎

【ReactNative】GraphQL Backend 構築 ❸ 〜Nexus Type 作成〜
Nexus Type を作成して GranphQL Type に変換させる方法を見ていきます。
スポンサーリンク

利用シーン

🎯 アプリケーションの起動に必要なデータ ( デフォルトの言語やデフォルトのジャンルなど ) をデータベースに一括入力できます。
🎯 開発環境でアプリケーションを検証して使用するための基本データを一括入力できます。

これは、開発データベースのリセットが必要になる場合がある Prisma Migrate を使用している場合に特に便利です。

prisma.seed キーを追加する

Prisma の統合シード機能は、package.json ファイルの prisma.seed キーに実行するコマンドを設定しておいて、prisma db seed がそれを実行します。

Line 7-9 を追加します。

// package.json

{
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "prisma": {
    "seed": "ts-node prisma/seed.ts"
  },
...

プロジェクトの prisma フォルダー内にシード スクリプト( seed.ts ) を記述します。

仮に以下のように Daibunrui、Chubunrui テーブルがあるとします。

大分類データを先に入力し、各大分類別 中分類でーたを入力することを想定します。

// 大分類
model Daibunrui {
  id     Int     @id @default(autoincrement())
  name  String  @unique
  daibunruis Chubunrui[]
}

// 中分類
model Chubunrui {
  id      Int    @id @default(autoincrement())
  name   String @unique
  daibunrui   Daibunrui? @relation(fields: [daibunruiId], references: [id])
  daibunruiId Int?
}

prisma/seed-sources.ts に入力データを作成します。(任意)

// prisma/seed-sources.ts

export const daibunrui = [
  { name: "HOGE" },
  { name: "FOO" },
  { name: "BAR" },
  { name: "BAZ" },
  ...
];

const hogeId = 1;
const fooId = 2;
const barId = 3;
const bazId = 4;
export const chubunrui = [
  { name: "hoge1", daibunruiId: hogeId },
  { name: "hoge2", daibunruiId: hogeId },
  ...
  { name: "foo1", daibunruiId: fooId },
  { name: "foo2", daibunruiId: fooId },
  ...
  { name: "bar1", daibunruiId: barId },
  { name: "bar2", daibunruiId: barId },
  ...
  { name: "baz1", daibunruiId: bazId },
  { name: "baz2", daibunruiId: bazId },
  ...
];

createMany() で一括入力できます。( SQLite はサポートしません )

skipDuplicates: true で重複データを排除します。

// prisma/seed.ts

import { PrismaClient } from "@prisma/client";
import { daibunrui, chubunrui } from "./seed-sources";

const prisma = new PrismaClient();

async function main() {
  try {
    await prisma.daibunrui.createMany({ data: [...daibunrui], skipDuplicates: true });
  } catch (error) {
    console.error(error);
  } finally {
    await prisma.chubunrui.createMany({ data: [...chubunrui], skipDuplicates: true });
  }
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

実行する

try🐶everything backend$ npx prisma db seed
Environment variables loaded from .env
Running seed command `ts-node prisma/seed.ts` ...

🌱  The seed command has been executed.
try🐶everything backend$ 

確認する

npx prisma studio コマンドで Prisma Studio を実行して確認します。

デフォルトで 5555 ポートを使用しますが、Android エミュレーターも同じポートを利用するので変更しておくと良いと思います。

$ npx prisma studio -p 5505

リレーションのある表を投入するときの注意(2026 年 7 月 追記)

この記事を書いたあとに気づいた点が 2 つあります。本文のコードをそのまま使うと、状況によってうまくいきません。

1. finally ではなく、順番に実行する

本文の main() は、大分類の投入を try、中分類の投入を finally に置いています。

finally は「エラーが起きても起きなくても実行される」ブロックです。つまり大分類の投入に失敗しても、中分類を入れにいきます。参照先がない状態で外部キーを持つデータを入れることになるので、そこでまた失敗します。

依存関係があるデータは、素直に順番に書いてください。

async function main() {
  await prisma.daibunrui.createMany({
    data: daibunrui,
    skipDuplicates: true,
  });

  await prisma.chubunrui.createMany({
    data: chubunrui,
    skipDuplicates: true,
  });
}

エラー処理は末尾の .catch() がすでに担っているので、main() の中で握りつぶす必要はありません。途中で失敗したら止まるほうが、原因も分かりやすくなります。

2. ID を決め打ちしない

本文では、大分類の ID を次のように書いています。

const hogeId = 1;
const fooId = 2;

これは「autoincrement() で 1 から順に振られるはずだ」という前提です。まっさらなデータベースに一度だけ流すなら、そのとおりになります。

ただし、すでにデータが入っている環境では成り立ちません。autoincrement の採番は、行を削除しても戻りません。開発中にデータを消して入れ直した経験があれば、次の投入では 1 から始まらない可能性があります。気づかないまま、間違った親に紐づいたデータができあがります。

確実なのは、ID ではなく名前で結びつける書き方です。

// 大分類を入れてから、名前を手がかりに ID を引く
await prisma.daibunrui.createMany({
  data: daibunrui,
  skipDuplicates: true,
});

const parents = await prisma.daibunrui.findMany();
const idOf = Object.fromEntries(parents.map((p) => [p.name, p.id]));

await prisma.chubunrui.createMany({
  data: [
    { name: 'hoge1', daibunruiId: idOf['HOGE'] },
    { name: 'foo1', daibunruiId: idOf['FOO'] },
  ],
  skipDuplicates: true,
});

本文のモデル定義では name@unique が付いているので、この方法が使えます。何度実行しても同じ結果になるのが利点です。

件数が少なければ、upsert を使って親子をまとめて書く方法もあります。どちらを選ぶかはデータ量次第です。

3. バージョンについて

この記事は 2023 年 10 月時点のものです。package.jsonprisma.seed を書いて npx prisma db seed で実行する、という流れは変わっていません。

ただし createMany のオプション(skipDuplicates など)や、seed スクリプトの実行に使うツールの推奨は、Prisma のバージョンによって変わることがあります。お使いのバージョンの公式ドキュメントとあわせてご確認ください。

※ 1 と 2 は本文に掲載したコードを読み直して整理したものです。3 を含め、改めて動作検証を行ったものではありません。

参考文献

Seeding your database

コメント

タイトルとURLをコピーしました