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


※ 当ページには【広告/PR】を含む場合があります。
2023/02/28
蛸壺の技術ブログ|ローカル環境できる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で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
        
これでお試しプロジェクトの準備は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
        

これで開発用のSQLiteの空のデータベースが
prisma/dev.dbとして生成されます。

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

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

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


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

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

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

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

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

公式|Prisma Client API reference

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)プログラミング入門〜これから学ぶ人のためのおすすめ書籍&教材の手引き