12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

prisma を勉強したときのまとめ(個人備忘録)

Last updated at Posted at 2023-07-03

はじめに

いきなり 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作成権限がないとエラーになる

.env
# mysql://ユーザー名:パスワード@localhost:3306/DB名
DATABASE_URL="mysql://root:prismatest@localhost:3306/testd02"

potsテーブルを作成するためにデータモデルの定義(Data model Definition)を記述する

schema.prisma
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:一度に複数のフィールドを削除

リレーションの設定

link
refarence

schema.prisma ファイルに追加記述

  • 1対Nの場合
    image.png
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
}

もしくはマッチングキーを利用

schema.prisma
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`);

複数カラムが同じテーブルにつながっている場合

リレーション名を設定して一意にする。

schema.prisma
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

  1. 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()
  })
  1. package.jsonにprismaのseedの設定を加えます。
"prisma": {
  "seed": "ts-node prisma/seed.ts"
}

複数ファイルに分割した場合以下のようにする。

"prisma": {
  "seed": "ts-node ./prisma/seed/start.ts"
}
  1. 実行
npx prisma db seed

おわりに

他の Prisma 記事です

12
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?