はじめに
いきなり prisma を学習することになりました
ORM とか全く知らないのに
いろんな記事読み漁って自分で技術検証できた部分をまとめるよ
ORM を調べた未来の自分より
デメリット(複雑すぎるクエリはしんどい)と
メリット(SQLインジェクション対策や型安全)があるけど
そんな大規模なプロジェクトじゃない限りSQL直書きでもいいらしい
あと、typescript 100% のWEBアプリケーション作るなら、typeORM のほうが良いってよ
参考文献
Node.js(Express.js)環境でPrisma ORMを使いこなすための基礎 一部old
node.js / Prisma ORM / mysql を試す例
Prisma Migrationの動作を確認してみた
prismaのschemaファイルの書き方(MySQL用)
マイグレーション|Prismaチュートリアル
[Prisma] チートシート - Qiita
prisma 入門 2022
概要
Prismaとは、Node.js/TypeScript環境で利用できるオープンソースのORMである。
ORM(Object Relational Mapping)とは、オブジェクトとデータベースをマッピングするための技術である。
つまりPrismaとは、データベースを効率的に管理し、簡単に操作を行うためのツールである。
Prismaでは、どのデータベースを利用しているか意識することなく、共通のオブジェクトメソッドを利用し、データベースを操作することができる。
Prismaはアプリケーションを構築する上で必須のツールではなりませんが、データベースの操作に関わるアプリケーション開発のスピードを飛躍的に向上させることができます。
機能構成
- Prisma Client
データベースの操作に利用する機能。
npx prisma generate
schemaを変更した際にズレが生じ、npm run dev した際に毎回エラーが出るのでpackage.json
に記述しておくと良い。
"scripts": {
"dev": "ts-node ./script.ts",
"prisma": "prisma generate"
},
- Prisma Migrate
Prismaの設定ファイル(schema.prisma)にデータモデルを記述しマイグレーションを行うことでデータベースにテーブルを作成することができます。
schema を作成した後に実行する。 - Prisma Studio
npx prisma studio
テーブルをブラウザ上で閲覧するためのUIも提供しておりブラウザ上からテーブル内のデータを編集することができます。モデル間のリレーションの関係もブラウザ上から把握することができます。
環境構築
TypeScript, Prisma, Expressのインストール
npde -v
npm -v
mkdir nodejs-prisma
cd nodejs-prisma
npm init -y
npm install typescript ts-node @types/node --save-dev
npx tsc --init
npm install express
npm install @types/express --save-dev # TypesScript用にExpress.jsの型定義をインストール
npm install prisma --save-dev
npx prisma # Prismaで利用できるコマンドのオプションを確認
npx prisma init # プロジェクトフォルダにprismaフォルダと.envファイルが作成される
# npx prisma init --datasource-provider sqlite # DBの設定変更(デフォルトはpostgresql)
# npx prisma init --datasource-provider mysql # DBの設定変更(デフォルトはpostgresql)
- prismaフォルダ内:設定ファイルであるschema.prismaファイル
- .envファイル:データベースに接続するために必要となる環境変数を設定する
DBの設定
schema.prisma
ファイルと .env
ファイル内を変更
DB作成権限がないとエラーになる
# mysql://ユーザー名:パスワード@localhost:3306/DB名
DATABASE_URL="mysql://root:prismatest@localhost:3306/testd02"
potsテーブルを作成するためにデータモデルの定義(Data model Definition)を記述する
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model pot {
id Int @id @default(autoincrement())
name String
score Int? @unique
email String @@map('e_mail')
@@map('pots')
}
- @id:主キー
- @default():デフォルト値セット
- autoincrement():自動採番
- ?:null許容
- @unique:カラムで一意の値
- @@unique([column1, column2]):複合キー
- @@map('e-mail'):独自の名前をマッピングできる
Prisma Schemaの書き方あれこれ
Prisma schema reference
types | types_schema | type attribute |
---|---|---|
varchar | String | @db.VarChar(X) |
char | String | @db.Char(X) |
TINYINT(1) | Boolean | @db.TinyInt(1) |
BIT(1) | Boolean | @db.Bit(1) |
datetime | DateTime | @db.DateTime() |
date | DateTime | @db.Date() |
time | DateTime | @db.Time() |
timestamp | DateTime | @db.Timestamp() |
マイグレーションの実行
shema.prisma に記述したモデルを元にデータベースにテーブルを作成すること
マイグレーションに任意の名前をつける必要がある
npx prisma migrate dev --name my_migrate_init
migration フォルダとその中に migration.sql
ファイルが生成される。
Prisma Studio でテーブルへのアクセス
npx prisma studio
ブラウザで localhost:5555
にアクセス
マイグレーションのリセット
テーブルを一度削除し再生成します。
npx prisma migrate reset
マイグレーションのpushとpull
# schema.prisma ファイルのモデルを変更した際に、migration ファイルを生成せずにテーブルを変更する。
npx prisma db push
# 現在のDBの状態を schema.prisma ファイルに反映させる。
npx prisma db pull
マイグレーションによるSeeding
Seeding ファイルを利用すると、マイグレーションの実行時にテーブルにデータを挿入できる。
prisma フォルダに seed.ts ファイルを作成しデータをテーブルに追加する処理を記述。
なんかよくわからん。諦🍈 → here
Express.js で Prisma のメソッドを利用
TSとJSが混在しています。
users テーブルを操作する関数。
findMany:全データを取得する
const prisma = new PrismaClient();
app.get('/users', async (req: Request, res: Response) => {
const users = await prisma.users.findMany();
return res.json(users);
});
create:データの登録
app.post('/users', async (req: Request, res: Response) => {
const { name, email } = req.body;
try {
const user = await prisma.user.create({
data: {
name,
email,
},
});
return res.json(user);
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError) {
if (e.code === 'P2002') {
console.log(
'There is a unique constraint violation, a new user cannot be created with this email'
);
}
}
return res.status(400).json(e);
}
});
update:データの更新
app.put('/users/:id', async (req: Request, res: Response) => {
const id = Number(req.params.id);
const { name } = req.body;
try {
const user = await prisma.users.update({
where: {
id,
},
data: {
name,
},
});
return res.json(user);
} catch (e) {
return res.status(400).json(e);
}
});
delete:データの削除
app.delete('/users/:id', async (req: Request, res: Response) => {
const id = Number(req.params.id);
try {
const user = await prisma.users.delete({
where: {
id,
},
});
return res.json(user);
} catch (e) {
return res.status(400).json(e);
}
});
findUnique:特定フィールドの取得
app.get('/users/:id', async (req: Request, res: Response) => {
const id = Number(req.params.id);
try {
const user = await prisma.users.findUnique({
where: {
id,
},
});
return res.json(user);
} catch (e) {
return res.status(400).json(e);
console.log('IDが存在しません。')
}
});
findFirst:最後に追加されたフィールドの取得
const user = await prisma.users.findFirst({
where: { name: 'Alice' },
})
upsert:指定したIDが存在する場合update、存在しない場合createを実行
const user = await prisma.users.upsert({
where: { id: 1 },
create: {
email: 'alice@prisma.io',
name: 'Alice',
},
update: {
email: 'alice@prisma.io',
name: 'AliceB',
},
})
createMany:一度に複数のフィールドの挿入
const users = await prisma.users.createMany({
data: [
{ name: 'Sonali', email: 'sonali@prisma.io' },
{ name: 'Alex', email: 'alex@prisma.io' },
],
})
updateMany:一度に複数のフィールドを更新
deleteMany:一度に複数のフィールドを削除
リレーションの設定
schema.prisma ファイルに追加記述
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
Posts Post[]
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id])
authorId Int
}
もしくはマッチングキーを利用
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
Posts Post[] @relation("author")
}
model Post {
id Int @id @default(autoincrement())
author User @relation("author", fields: [authorId], references: [id])
authorId Int
}
下記の sql と同等
-- テーブルの外部キー制約
-- 1対NのNの方のテーブルにキー制約をかける。
ALTER TABLE `Post`
ADD CONSTRAINT `author` FOREIGN KEY (`authorId`) REFERENCES `User` (`id`);
複数カラムが同じテーブルにつながっている場合
リレーション名を設定して一意にする。
model videos {
V_id Int @id @default(autoincrement())
V_title String @db.VarChar(255)
V_VP_a video_pairs[] @relation("a")
V_VP_b video_pairs[] @relation("b")
}
model video_pairs {
VP_id Int @id @default(autoincrement())
VP_a_video_id Int
VP_b_video_id Int
VP_V_a videos @relation(fields:[VP_a_video_id], references:[V_id], name: "a")
VP_V_b videos @relation(fields:[VP_b_video_id], references:[V_id], name: "b")
}
VIEW or JOIN
Migrate を使用してスキーマ内のビューをデータベースに適用することはまだできません
手動でなんかいろいろせにゃならんでめんどくさいみたいです。
prisma.jsで覚えたメモ
How to include views in your Prisma schema
PrismaのJOINの挙動を観察
How to create a prisma model from a SQL view - Stack Overflow
データベース ビューのサポートを追加 GitHub
PrismaからRDBのViewを参照する
DBに別の方法でViewを作成し schema を記述すると参照できる。
import { PrismaClient } from '@prisma/client';
(async () => {
const prisma = new PrismaClient({
log: ['query'],
});
const params = await prisma.user.findMany({
select: {
id: true,
name: true,
UserConfig: {
select: {
category: true
}
}
},
where: {
UserConfig: {
category: {
in: ['01', '02']
}
}
}
});
console.log(params);
})();
prisma/seed.ts feat. Relations
PrismaでSeedでデータを投入する
prismaのseedを複数ファイルに分割する
Seeding with relations?
Relations (Reference)
Seeding your database
- seed ファイルを用意
複数ファイルに分割したい場合、 seed ディレクトリを作成し、中にファイルを用意
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
const alice = await prisma.users.upsert({
where: { U_id: 1 },
update: {},
create: {
U_name: 'alice@prisma.io',
U_age: 21,
U_gender: 0,
},
});
console.log({ alice });
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
または
import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
// モデル投入用のデータ定義
const userData: Prisma.UserCreateInput[] = [
{
name: 'hoge1',
email: 'hoge1@example.com',
},
{
name: 'hoge2',
email: 'hoge2@example.com',
},
{
name: 'hoge3',
email: 'hoge3@example.com',
},
]
const transfer = async () => {
const users = [];
for (const u of userData) {
const user = prisma.user.create({
data: u,
})
users.push(user);
}
return await prisma.$transaction(users);
}
// 定義されたデータを実際のモデルへ登録する処理
const main = async () => {
console.log(`Start seeding ...`)
await transfer();
console.log(`Seeding finished.`)
}
// 処理開始
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})
- package.jsonにprismaのseedの設定を加えます。
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
複数ファイルに分割した場合以下のようにする。
"prisma": {
"seed": "ts-node ./prisma/seed/start.ts"
}
- 実行
npx prisma db seed
おわりに
他の Prisma 記事です