【Prisma入門】ローカル環境できるPrismaを使い始めたい人のためのRDB操作ガイド


※ 当ページには【広告/PR】を含む場合があります。
2023/02/28
2025/06/28
RustエンジンレスなPrisma/SQLiteクライアントを外部モジュールとして利用する
蛸壺の技術ブログ|ローカル環境できるPrismaを使い始めたい人のためのRDB操作ガイド

PrismaはNode.js&Typescriptで実装する代表的な「オブジェクト関係マッピング(ORM)」ライブラリです。

            リレーショナルデータベース(RDB)とプログラミング言語の間の
非互換なデータを変換するプログラミング技法
        
現在、PrismaがサポートするRDBは主に以下のリストしているタイプがあります。

            PostgreSQL
MySQL
SQLite
SQL Server
MongoDB
CockroachDB
        
最初にローカル環境でサッと試すのに敷居が低いと勝手に思っているのは、ブラウザでも親和性の高い「SQLite」です。

Prisma公式のQuick StartでもSQLiteを最初に使った例で紹介されています。

参考|Quickstart

今回はPrismaを扱いをTypescriptから操作するための始め方〜使い方をまとめていきます。

以下、説明していくポイントになります。

            1. Prismaプロジェクトの構築方法を理解する
2. Prismaクライアントの各種メソッドの使い方(CRUD)を学ぶ
    レコード作成(Create)系 ... create/createMany
    レコード削除(Delete)系 ... delete/deleteMany
    レコード取得(Read)系 ... findUnique/findFirst/findMany
    レコード更新(Update)系 ... update/upsert/updateMany
        

合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

Prismaの基本的な作業の流れ

最初にPrismaに触れてみたい・使ってみたいという方にとって、Prisma特有のワークフローシステムに慣れるまではとっつきにくいように思います。

この辺は
公式サイトの解説を引用として紹介しておきます。

Prisma Migrate

PrismaでDBを開発していく上、DB設計者からみた基本的な作業の流れが、『Prisa Migrate』(
prisma migrate devコマンド)を使うやり方です。

以下の作業の流れの模式図で説明していきます。

合同会社タコスキングダム|蛸壺の技術ブログ

作業の流れは上から下に見ていきますが、最初の項目1においては、「Prismaスキーマ(schema.prismaファイル)」に修正を加える作業を表しています。

Prismaのスキーマに関しては以下のページを参照してください。

参考|Prisma Schema

スキーマを編集した後で、項目2の「prisma migrate devコマンド」を実行することで、

            + データベースを新規作成(データベースがない場合)
+ データベースへ新しいスキーマを更新(データベースが存在している場合)
+ Prismaクライアントの生成
        
が同時に実行されます。

なお、既存のデータベースへmigrateが適用された場合、既に登録していたデータ(レコード)があった場合、それらのデータは初期化されて消失しますので、消したくないデータが場合には必ずバックアップを取っておきましょう。

こうして出来上がったDBを使って、項目3で示すように、本番用のアプリケーションが使えるように適切なレポジトリサービスに配置し、Prismaクライアントから使えるようにする作業まで行うところまでが、Prismaの基本的なワークフローになります。

Migrate済みのDBからスキーマを抽出してクライアントへ反映する

DB設計者以外で、データをDBから利用する目的が主のアプリケーション開発者にとって、何らかの理由で、Prismaクライアントが無い場合、データを利用できないと困る場合があります。

例えば普段使っていたDBの構造が変更されていたときには、それまで利用していたPrimsaクライアントが使えないようになるかもしれません。

そのような状況では、Prisaクライアントを更新する作業の流れは以下のようになります。

合同会社タコスキングダム|蛸壺の技術ブログ

まず一番左側の模式図では、DB開発者によってデータベースの仕様が変更された状況にあるとします。

そこで、アプリケーション開発者は
「prisma db pull」コマンドを実行し、Prismaスキーマを生成・更新することができます。

そのスキーマを使い、
「prisma generate」コマンドでPrismaクライアントを再構築することが可能です。

あとは、そのクライアント利用することで、アプリケーションも再ビルドすると継続して利用することができます。

MigrateとDB-Push/Generateの違い

Prismaでデータベースを設計・生成する場合、「prisma migrate dev」コマンドは、「prisma db push/prisma generate」を使っても同じことができます。

            $ prisma migrate dev

#👇「ほぼ」等価な操作

$ prisma db push
$ prisma generate
        

参考|DB-Pushコマンド

参考|Generateコマンド

このMigrateとDB-Push/Generateの違いは、DBの編集ポイントに「履歴」が付くか付かないか、です。

これはGitコマンドに使い慣れた方には馴染みのある考え方で、Migrateでの変更がコミット・マージ相当であるのに対し、DB-Pushは単なるプッシュでしかありません。

そのため、DB設計に問題があった場合、ある時点までの設計バージョンまで戻りたいとき、Migrateで変更履歴をきっちり管理していれば、指定した履歴までスキーマを戻すことができます。

他方、スキーマの変更をDB-Pushでした行っていない場合、途中で以前のスキーマへ戻したいと思っても、戻すことはできません。

どちらを使うかは設計者の裁量で決められますが、大きな仕様変更が伴う修正はMigrateを、スペルミスなど細かい修正はDB-Pushを使うと良いでしょう。


合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

PrismaでSQLiteを使ってみる

Nodejsが使える環境で、Prisma-SQLiteを使うプロジェクトを構築するところから始めましょう。

なおここではパッケージマネージャは
yarnで統一します。

シェル端末から以下のコマンドを順に実行していきましょう。

            $ mkdir prisma-sqlite
$ cd prisma-sqlite/
$ yarn init -y
$ yarn add typescript ts-node @types/node -D
$ npx tsc --init
$ yarn add prisma -D
$ npx prisma init --datasource-provider sqlite
        
うまく初期化されると、プロジェクトにprisma/schema.prismaというファイルが挿入されます。

            // This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
  output   = "../lib/generated/prisma"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
        
これがPrismaのスキーマになる重要なファイルです。

もう一つ重要な設定が
.envに環境変数として自動で追加されているはずです。

            DATABASE_URL=file:./dev.db
        
Prisma用の環境変数・「DATABASE_URL」Connection URLと呼ばれるパラメーターです。

参考|Connection URLs

上の例はSQLiteの例ですが、SQLの種類によって正しく記述する必要があります。

余談ですが、お手元の環境によって「prisma init」コマンドが既存の.envファイルがあった場合、自動挿入失敗することがあります。

            #This was inserted by `prisma init`:
[object Promise]
        
...バグ?であるように思うので、このような場合には手動で.envファイルを修正してみてください。

ともかくこれでお試しプロジェクトの準備はOKです。

Prismaスキーマを定義する

Prismaのデータの構造モデルは
Prismaスキーマと呼ばれ、schema.prismaというファイルで管理します。

先程の
prisma initコマンドからprismaというフォルダが自動生成されて、その中にschema.prismaのテンプレートが生成されていると思います。

ひとまず公式に従って以下のようにPrismaスキーマのモデルを追加してみます。

            // This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

//👇ここから追記
model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User    @relation(fields: [authorId], references: [id])
  authorId  Int
}
        
このファイルはPrismaが使うデータベースの設計図ですので、プロジェクトには必ず1つ必要になります。

Prismaスキーマから空のデータベースをマイグレする

Prismaスキーマを使ってSQLiteデータベースを作成します。

Prismaでは、データベースを作成したり、別のスキーマからデータベースをアップデートしたりする操作は主に
「prisma migrate」コマンドが担当します。

参考 | prisma-migrate

prisma migrateのサブコマンドはいくつか種類がありますが、このうち開発環境からデータベースを構築していく場合にはdevサブコマンドが利用できます。

devサブコマンドは開発中・移植作業中のデータベースを更新したり、データベースが存在しない場合には新規のデータベースを作成することができます。

詳しいコマンドオプションは
npx prisma migrate dev --helpから確認できます。

ではプロジェクトのPrismaスキーマから開発用のSQLiteデータベースを新規作成してみましょう。

            $ npx prisma migrate dev --name hoge
        
Migrateした場合にはSQLiteの空のデータベース・prisma/dev.dbと、Prismaクライアントのソースコードがnode_modules/@prisma/client以下に合わせて生成されます。

DB-Pushからデータベースだけ生成したい場合、

            $ npx prisma db push
        
とすることで、データベースが新規作成されます。

データベースの開発中は、データベースの中身をチェックする手段やツールがあると便利です。

VSCodeをお使いの場合には、パッとデータベースの中身を閲覧できる
SQLite Viewerというエクステンションがおすすめです。

合同会社タコスキングダム|蛸壺の技術ブログ


合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

Prismaクライアントでテーブルを操作する

先程の空のdev.dbをテーブルを使って、煮るなり焼くなり色々といじって遊んでみましょう。

データベースのテーブルにレコードを追加したり、更新したりという操作は
「Prismaクライアント(@prisma/client)」の担当になります。

上述した
prisma migrate devprisma db pushした時点で自動で@prisma/clientがインストールされる仕様ですが、Prismaクライアントだけが個別に必要な場合には以下のように導入します。

            $ yarn add @prisma/client
        

PrismaクライアントのCLIドキュメントは以下にあります。

公式|Prisma Client API reference

もし、先ほどの説明した通り、MigrateでなくDB-Pushからデータベースのみを生成した場合、まだPrismaクライアントのソースコードが生成されていない場合があります。

スキーマからPrismaクライアントを生成したい場合、

            $ npx prisma generate
        
を実行することで、node_modules/@prisma/client以下にソースコードが生成されます。

ではPrismaクライアントを使って
typescriptベースのスクリプトを作って操作してみましょう。

【Create系】データベースのテーブルへレコードを一つ追加する

最初に
dev.dbUserテーブルに適当なレコードを追加します。

追加のためのスクリプトを
pushOne.tsという名前で作成しておきます。

            $ touch pushOne.ts
        
公式にならって、以下のように実装しましょう。

            import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
    const user = await prisma.user.create({
        data: {
            name: 'Alice',
            email: 'alice@example.com'
        }
    });
    console.dir(user);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
ではこのスクリプトをts-nodeで実行します。

            $npx ts-node pushOne.ts
{ id: 1, email: 'alice@example.com', name: 'Alice' }
        
合同会社タコスキングダム|蛸壺の技術ブログ

ちゃんとレコードが追加されて増えているのが確認できます。

こんな感じでPrismaクライアントからテーブルインスタンス(
prisma.<テーブル名>)を指定し、定義済みメソッドを呼び出すことでデータベースの操作が可能になっています。

Prismaに限らずデータベース構築を学習する場合、ちまちまとレコードのフィールドを手入力するのは面倒ですので、ここで
faker-jsを導入します。

            $ yarn add @faker-js/faker -D
        

もう一つスクリプトを
pushRandom.tsという名前で追加しておきましょう。

            $ touch pushRandom.ts
        
これを以下のようにします。

            import { PrismaClient } from "@prisma/client";
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
    const user = await prisma.user.create({
        data: {
            name: faker.name.firstName(),
            email: faker.internet.email(),
        }
    });
    console.dir(user);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
tsスクリプトを連続して動かしてみましょう。

            $ npx ts-node pushRandom.ts
{ id: 2, email: 'Uriel2@gmail.com', name: 'Mattie' }
$ npx ts-node pushRandom.ts
{ id: 3, email: 'Ethyl_Price93@yahoo.com', name: 'Vivienne' }
$ npx ts-node pushRandom.ts
{ id: 4, email: 'Floyd_Mohr60@hotmail.com', name: 'Gregorio' }
$ npx ts-node pushRandom.ts
{ id: 5, email: 'Kaycee_Schmitt@gmail.com', name: 'Julien' }
        
ランダムに割り当てられた値でレコードが次々に生成されているのが分かります。

合同会社タコスキングダム|蛸壺の技術ブログ

【Create系】データベースのテーブルへレコードを一括追加する(バルクインサート)

先程は一つづつしかレコードの追加が出来ませんでしたが、
createManyメソッドを使うと一括でまとめて追加(バルクインサート)することもできます。

が、
SQLiteではcreateManyメソッドはサポートされませんのでご注意ください。

tsスクリプトを以下のように準備します。

            import { PrismaClient } from "@prisma/client";
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();
const count = 5;

async function main() {
    const user = await prisma.user.createMany({
        data: [...Array(count)].map(() => {
            return {
                name: faker.name.firstName(),
                email: faker.internet.email(),
            }
        })
    });
    console.dir(user);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
なので、SQLiteでのバルクインサートはしかたないので、createメソッドを非同期でPromise.allさせます。

            import { PrismaClient } from "@prisma/client";
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();
const count = 5;

async function main() {
    await Promise.all(
        [...Array(count)].map(async () => {
            await prisma.user.create({
                data: {
                    name: faker.name.firstName(),
                    email: faker.internet.email(),
                }
            });
        })
    );
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
このスクリプトをnpx ts-node pushMany.tsで実行すると、

合同会社タコスキングダム|蛸壺の技術ブログ

と期待通りにレコードが増えています。

【Delete系】データベースのテーブルからレコードを一つ削除する

次にテーブルからレコードの削除もやってみましょう。

テーブルをから指定したレコードを削除するメソッドは
deteleメソッドです。

削除指定するレコードは、

            1. ID
2. 一意に決まる属性値
        
のどちらかを使います。

            import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
    const deleteUser = await prisma.user.delete({
        where: { id: 1 }
    });
    console.dir(deleteUser);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
ここでは、deleteメソッドに指定している中身のオブジェクトのwhere: {id: 1}という箇所が、削除対象のIDを指定していることになります。

実行すると、

            $npx ts-node deleteOne.ts
{ id: 1, email: 'alice@example.com', name: 'Alice' }
        
と削除したレコードがあれば返り値で取得することで削除確認にもなります。

deleteメソッドはRDBによくあるWHERE句によるフィルタリングをしている感覚で利用することが出来ます。

合同会社タコスキングダム|蛸壺の技術ブログ

【Delete系】データベースのテーブルからレコードを一括削除する

deleteManyメソッドを使えば、ある条件を満たすレコードを複数削除することもできます。

            import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
    const deleteUsers = await prisma.user.deleteMany({
        where: {
            id: {
                gte: 7
            }
        }
    });
    console.dir(deleteUsers);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
このスクリプトだと、where: { id: {gte: 7} }(IDが7以上)という条件を満たすレコードを一括して削除しています。

実行すると、

            $ npx ts-node deleteMany.ts
{ count: 4 }
        
ここでの返り値はcount: 4という表示ですが、deleteManyでは削除したレコード数の結果のみが返されます。

【Read系】テーブル内を検索してレコードを一つ取得する

テーブルの中を検索してレコードを見つけるメソッドとしては、
findUniquefindFirstの2つがあります。

            findUnique:
    IDか一意に識別できる条件を指定してレコードを取得
findFirst:
    指定した条件に一致する最初のレコードを取得
        
結果としては条件にヒットしたレコードが一つ返るわけですが、テーブル中に1つしかないものを探すならfindUnique、条件によっては複数存在してもよいものをfindFirstで使い分けします。

ここでは
findUniqueを例にとって試してみましょう。

            import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
    const foundUser = await prisma.user.findUnique({
        where: { id: 4 }
    });
    console.dir(foundUser);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
先程説明したdeleteメソッドと考え方は同じで、findUniqueメソッドの引数にフィルター条件のクエリをwhereフィールドに指定することで該当のレコードを検索できます。

            $ npx ts-node findOne.ts
{ id: 4, email: 'Floyd_Mohr60@hotmail.com', name: 'Gregorio' }
        

【Read系】テーブル内の検索して条件を満たすレコードを全て取得する

こちらは
findFirstの全体版で、条件に一致する全てのレコードをリスト取得するfindManyというメソッドを使います。

            import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

async function main() {
    const foundUsers = await prisma.user.findMany({
        where: { id: {lte: 6} }
    });
    console.dir(foundUsers);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
実行すると、

            $ npx ts-node findMany.ts
[
  { id: 2, email: 'Uriel2@gmail.com', name: 'Mattie' },
  { id: 3, email: 'Ethyl_Price93@yahoo.com', name: 'Vivienne' },
  { id: 4, email: 'Floyd_Mohr60@hotmail.com', name: 'Gregorio' },
  { id: 5, email: 'Kaycee_Schmitt@gmail.com', name: 'Julien' },
  { id: 6, email: 'Destinee89@gmail.com', name: 'Raphaelle' }
]
        
検索したレコードがリスト(配列)で返されていることが分かります。

【Update系】テーブルの一つのレコードの更新する

テーブルの基本的な操作の中で、
「作成」「削除」「取得」を順に説明しました。

残る最後は
「更新」の操作です。

特定の一つのレコードの更新は、
updateupsertを使います。

            update:
    IDまたは一意に決まる条件に一致するレコードを更新する。
    テーブルに存在しない場合には何もしない
upsert:
    IDまたは一意に決まる条件に一致するレコードを更新する。
    テーブルに存在しない場合にはレコードを新規追加する
        
基本的な使い方は2つのメソッドとも変わらないのですが、テーブル内に目的のレコードがあるかないかで結果が違うことに注意して使い分けてください。

ここでは
updateメソッドの使用例を示します。

            import { PrismaClient } from "@prisma/client";
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
    const updatedUser = await prisma.user.update({
        where: {id: 2},
        data: {
            name: faker.name.firstName(),
            email: faker.internet.email(),
        }
    });
    console.dir(updatedUser);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
実行すると、

            $ npx ts-node updateOne.ts
{ id: 2, email: 'Sheila94@gmail.com', name: 'Elvera' }
        
というように変更後のレコードが表示されればOKです。

【Update系】テーブル中の条件を満たすレコードを一括更新(バルクアップデート)する

先程の
updateの全体版にあたるupdateManyメソッドを使います。

updateManyテーブル中の条件に一致する全てレコードを更新(バルクアップデート)します。

条件にヒットした値の特定のフィールドを全て同じ値に一括更新したい場合に使えます。

例えば下はあまり意味がないコードかもしれませんが、emailのフィールド値に
gmailを含んだレコードを検索し、その全てのnameをなにか同一の名前に書き換えるようなスクリプトを作ってみましょう。

            import { PrismaClient } from "@prisma/client";
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() {
    const updatedUserCount = await prisma.user.updateMany({
        where: { email: {contains: 'gmail'} },
        data: {name: faker.name.firstName()}
    });
    console.dir(updatedUserCount);
};

main()
    .catch(e => {
        console.error(e);
        process.exit(1);
    })
    .finally(async () => {
        await prisma.$disconnect();
    });
        
スクリプトを実行すると、書き換えに成功したレコード数が返ってきます。

            $ npx ts-node updateMany.ts
{ count: 3 }
        
更新の結果は見ての通りです。

合同会社タコスキングダム|蛸壺の技術ブログ

updateManyメソッドを使う場合、条件に該当するレコードの全てを書き換えられるのは、同時で同一の値のみです。

レコードに応じて一つ一つ個別に特定の値を書き換える場合、
updateメソッドをPromise.allする方法のほうがよいでしょう。

updateManyは簡単なフラグの一括書き換え(true⇔false)などには使えそうです。


合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き

まとめ

以上、今回は「Prisma」の導入から使い方の基本までを広く浅くすくってみました。

話のポイントをまとめると、

            1. Prismaプロジェクトの構築方法を理解する
2. Prismaクライアントの各種メソッドの使い方(CRUD)を学ぶ
    レコード作成(Create)系 ... create/createMany
    レコード削除(Delete)系 ... delete/deleteMany
    レコード取得(Read)系 ... findUnique/findFirst/findMany
    レコード更新(Update)系 ... update/upsert/updateMany
        
Prismaのすごいところはtypescriptでのデータベース操作の実装をマスターすれば、サポートされるRDBに特化した知識が無くともそれなりにデータベースが使えてしまうことにあります。

これからますますフロント・バックエンドエンジニアとデータベースエンジニアの境がなくなっていくのかもしれません。
記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。

合同会社タコスキングダム|蛸壺の技術ブログJavascript(js)&Typescript(ts)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き