初めてのPrisma+MongoDB

# はじめに
Node.jsのORMであるPrismaを使用してみようと言うことでローカルにMongoDBを立てて使ってみました。

動かした結果、今回はlocalhostで行ってみましたが、MongoDBはdockerで動作させないとinsertで問題がありました。手順に沿って構築しようとしている方はご注意ください。

~~ 最後にdockerで成功した方法を追記しています ~~

## MongoDB
Prismaに入る前にMonogDBをlocalhostで構築します。

MongoDBはOSSのDBですが、2018年にSSPLのライセンスに変わっているため、商用利用する方は4.0.3を使用することになります。

今回は商用利用するわけではないため公式から最新版をインストールします。

https://www.mongodb.com/docs/manual/tutorial/


### 起動・終了

MongoDBの起動・終了コマンド

```shell
# 起動
brew services start mongodb-community@7.0
# 終了
brew services stop mongodb-community@7.0
```

### GUIツール

[MonoDB Compass](https://www.mongodb.com/ja-jp/products/tools/compass) か Visual Studio Codeの拡張機能を使用します。

VSCode拡張機能を使用する場合、Connectionで以下のURLを追加します。
```
mongodb://localhost:27017/
```
### 設定

不要です。

今回は初めからあるadminのDBを使用しますが、自作のDBにしたい場合はDBを追加します。

Prismaがcollectionも作成してくれます。


## Prisma

prismaでmongoDBを使用するサンプルのnodeプロジェクトを作成し、Prismaをインストールします。

```shell
# nodeプロジェクト作成
npm init prisma-test
# Prismaインストール
npm i @prisma/client
# 初期設定
npx prisma init
```

prismaフォルダと中にshema.prismaファイルが作られます。
datasourceをmongodbに変更してmodelを追加します。

```schema.prisma
generator client {
  provider = “prisma-client-js”
}
datasource db {
  provider = “mongodb”
  url      = env(“DATABASE_URL”)
}
model User {
  id    String @id @default(auto()) @map("_id") @db.ObjectId
  email String @unique
  name  String?
}
```

init時に.envファイルも作成されているのでDATABASE_URLを設定します。
DB名まで必要です。

```.env 
DATABASE_URL="mongodb://localhost:27017/admin"
```

URLの設定は以下を参照

https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/mongodb/connect-your-database-typescript-mongodb

これで準備が整いましたので、以下コマンドを実行します。

```shell
npx prisma db push 
```

MongoDBを見るとadminのDBの中にUserのcollectionが作成されていることが確認できます。

めっちゃ便利ですね。

### データの取得

MongoDBのUserのcollectionに1つデータを作成しておきます。
```
{
    email: "user@example.com",
    name: "user"
}
```

index.tsを作成して以下のコードを準備します。

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

const prisma = new PrismaClient()

async function main() {
  // ... you will write your Prisma Client queries here

  const allUsers = await prisma.user.findMany()
  console.log(allUsers)
}

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

```

実行します。

```shell
npx ts-node index.ts
```

結果が返ってきます。
```
[
  {
    id: '6605ffe06eadfa08ae9250b1',
    email: 'user@example.com',
    name: 'user'
  }
]
```

### データの追加

main関数の中に以下のcreateを追加します。

```ts
  await prisma.user.create({
    data: {
      name: 'Rich',
      email: 'hello@prisma.com'
    },
  })
```

同様に実行するとエラーが発生します。

```
  4 
  5 async function main() {
  6   // ... you will write your Prisma Client queries here
→ 7   await prisma.user.create(
Prisma needs to perform transactions, which requires your MongoDB server to be run as a replica set. https://pris.ly/d/mongodb-replica-set
    at In.handleRequestError (/Users/xxxx/work/next/prisma-sample/node_modules/@prisma/client/runtime/library.js:122:6854)
    at In.handleAndLogRequestError (/Users/xxxx/work/next/prisma-sample/node_modules/@prisma/client/runtime/library.js:122:6188)
    at In.request (/Users/xxxx/work/next/prisma-sample/node_modules/@prisma/client/runtime/library.js:122:5896)
    at async l (/Users/xxxx/work/next/prisma-sample/node_modules/@prisma/client/runtime/library.js:127:10871)
    at async main (/Users/xxxx/work/next/prisma-sample/index.ts:7:3) {
  code: 'P2031',
  clientVersion: '5.11.0',
  meta: { modelName: 'User' }
}
```

確認したところMongoDBのレプリカセットが必要とのこと。。。

https://stackoverflow.com/questions/76776691/prisma-needs-to-perform-transactions-which-requires-your-mongodb-server-to-be-r

localhostで設定も可能のようですが、うまくいっていないので初めからDockerを使った方が良かったと言う結論になります。

ORMを使用するとDBアクセスの実装がわかりやすくなりますね。
Prismaは設定も簡単で他のDBもサポートしているので勉強の手間や実装が省けて便利です。

## 追記
改めて確認しました。
dockerを使うだけでは解決できず、レプリカセットの設定が必要で以下のコマンドで動作確認できました。
```shell
#1 - mongo コンテナを起動します
docker run --name mongodb -d -p 27017:27017 mongo mongod --replSet rs0

#2 - mongodb コンテナーが起動して実行されたら、「mongosh」と入力します。
docker exec -it mongodb mongosh

#3 - mongosh 内でレプリカ セットを開始します
rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'localhost:27017'}]})
```

https://github.com/prisma/prisma/issues/8266#issuecomment-1463343163

起動して失敗していたデータ追加を実行すると無事成功しました。

----

公式ページ

https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch