Prisma を触ってみる

Typescript 製 ORM の Prisma の主要機能を触ってみたいと思います。
チュートリアルの[データベースを新規に作成する開発パターン](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch) をベースに機能を試して行きます。

# 初期設定
- 必要なものを install する
    - devDependencies
        - `typescript`
        - `prisma`
        - `ts-node`
        - `@types/node`
    - dependencies
        - `@prisma/client`
- `npx prisma init` する
- [DB の接続情報を設定する](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/connect-your-database-typescript-postgres)

# migrate

[こちら](https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/using-prisma-migrate-typescript-postgres#create-database-tables-with-prisma-migrate)を参考に schemaファイル (schema.prisma) に user, post, profile の schema を記載します。


https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/using-prisma-migrate-typescript-postgres

次に上記を参考に `npx prisma migrate dev --name init` を実行します。

```
$ npx prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": MySQL database "mydb" at "db:3306"

- Drift detected: Your database schema is not in sync with your migration history.

The following is a summary of the differences between the expected database schema given your migrations files, and the actual schema of the database.

It should be understood as the set of changes to get from the expected schema to the actual schema.

[+] Added tables
  - Post
  - Profile
  - User

[*] Changed the `Post` table
  [+] Added foreign key on columns (authorId)

[*] Changed the `Profile` table
  [+] Added foreign key on columns (userId)

- The following migration(s) are applied to the database but missing from the local migrations directory: 20211231005805_init

✔ We need to reset the MySQL database "mydb" at "db:3306".
Do you want to continue? All data will be lost. … yes

Applying migration `20211231010444_init`

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20211231010444_init/
    └─ migration.sql

Your database is now in sync with your schema.

✔ Generated Prisma Client (3.7.0 | library) to ./node_modules/@prisma/client in 282ms
```

4つの table が作成されていることを確認できました。

```
MySQL [mydb]> show tables;
+--------------------+
| Tables_in_mydb     |
+--------------------+
| Post               |
| Profile            |
| User               |
| _prisma_migrations |
+--------------------+
4 rows in set (0.005 sec)
```

`migrate dev` の dev とは[開発環境で実行するモード](https://www.prisma.io/docs/concepts/components/prisma-migrate#development-environments)のようです。

`migrate dev` は migration ファイルを作成した後にすぐに適用されます。

`--create-only` を追加することで migration ファイルを作成するだけに留めることができます。

https://www.prisma.io/docs/concepts/components/prisma-migrate#customizing-migrations

# データを読み取る

DBにデータを追加します。

```
INSERT INTO `User` (`email`, `name`) VALUES ('hoge@example.com', 'hoge');
```

以下 index.ts を用意します。

``` index.ts
import {PrismaClient} from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
    const allUsers = await prisma.user.findMany()
    console.log(allUsers)
}

main()
    .catch((e) => {
        throw e
    })
    .finally(async () => {
        await prisma.$disconnect()
    })
```

以下のように読みとることができます。

```
$ npx ts-node src/index.ts
[ { id: 1, email: 'hoge@example.com', name: 'hoge' } ]
```

# データの書き込み

先ほどの index.ts の main を書き換えて実行します。

``` index.ts
async function main() {
  await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
      posts: {
        create: { title: 'Hello World' },
      },
      profile: {
        create: { bio: 'I like turtles' },
      },
    },
  })

  const allUsers = await prisma.user.findMany({
    include: {
      posts: true,
      profile: true,
    },
  })
  console.dir(allUsers, { depth: null })
}
```

```
$ npx ts-node src/index.ts
[
  {
    id: 1,
    email: 'alice@prisma.io',
    name: 'Alice',
    posts: [
      {
        id: 1,
        createdAt: 2021-12-31T08:29:02.391Z,
        updatedAt: 2021-12-31T08:29:02.392Z,
        title: 'Hello World',
        content: null,
        published: false,
        authorId: 1
      }
    ],
    profile: { id: 1, bio: 'I like turtles', userId: 1 }
  }
]
```

新規作成した user と紐付いた, post, profile が作成されていることがわかります。



# client を更新する

model を更新した際、`prisma generate` で更新します。

> After you change your data model, you'll need to manually re-generate Prisma Client to ensure the code inside node_modules/.prisma/client gets updated: $ prisma generate

https://www.prisma.io/docs/concepts/overview/what-is-prisma#generating-prisma-client

[ここに](https://www.prisma.io/docs/concepts/overview/what-is-prisma#prisma-migrate)わかりやすい更新のフローがあります。



試しに先ほどの model User に address カラムを追加し、`prisma generate` を実行します。 

```
npx prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma

✔ Generated Prisma Client (3.7.0 | library) to ./node_modules/@prisma/client in 324ms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
```

クライアントが更新されたので、エディタの complete で表示してくれるようになりました。

![スクリーンショット 2022-01-01 22.21.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/5360ab75-7cf5-17c5-8f3d-f03e5b33abb1.png)



# transaction 

transaction も利用することができます

https://www.prisma.io/docs/guides/performance-and-optimization/prisma-client-transactions-guide


# data load （seeding）

データのロード機能もあります。
Prisma モデルを利用した TypeScript ファイルを実行したり、shell で実行することができるようです。

https://www.prisma.io/docs/guides/database/seed-database

試してみます。
`prisma/seed.ts` を作成して以下のコードを貼り付けます。

```seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

async function main() {
    const alice = await prisma.user.upsert({
        where: { email: 'alice@prisma.io' },
        update: {},
        create: {
            email: 'alice@prisma.io',
            name: 'Alice',
            posts: {
                create: {
                    title: 'Check out Prisma with Next.js',
                    content: 'https://www.prisma.io/nextjs',
                    published: true,
                },
            },
        },
    })

    const bob = await prisma.user.upsert({
        where: { email: 'bob@prisma.io' },
        update: {},
        create: {
            email: 'bob@prisma.io',
            name: 'Bob',
            posts: {
                create: [
                    {
                        title: 'Follow Prisma on Twitter',
                        content: 'https://twitter.com/prisma',
                        published: true,
                    },
                    {
                        title: 'Follow Nexus on Twitter',
                        content: 'https://twitter.com/nexusgql',
                        published: true,
                    },
                ],
            },
        },
    })
    console.log({ alice, bob })
}

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

package.json ファイルに seeding script をキックするための設定を追加します。

```
"prisma": {
    "seed": "ts-node prisma/seed.ts"
  },
```

`npx prisma db seed` で seeding を実行します。 

```
$ npx prisma db seed 
Environment variables loaded from .env
Running seed command `ts-node prisma/seed.ts` ...
{
  alice: { id: 1, email: 'alice@prisma.io', name: 'Alice' },
  bob: { id: 2, email: 'bob@prisma.io', name: 'Bob' }
}

🌱  The seed command has been executed.
```

:seedling:  が芽生えてデータをロードすることができました。


# 触った感想
- model が型で補完をしてくれるので快適
- エラーメッセージが丁寧
- ドキュメントが丁寧

ORM は使い込まないと評価はできないと思うのですが、
少し触ってみた感想としてはとても良かったので続けて触ってみたいと思います。