Prisma + PostgreSQLでつまづいたテーブル名の話

Prismaが面白そうなので使ってみたら、PostgreSQLのコマンドだとテーブルの中身がうまく表示されなくて困ったときの話です。

超初歩的な話ですが、同じように詰まった際の手助けになればと思います。

# 結論

PostgreSQLだとテーブル名は頭文字小文字がデフォルトの想定（内部的に勝手に変換される）なので、頭文字大文字のテーブル名の場合、ダブルクオーテーション（””）で囲わないといけないみたいです。

```sql
SELECT * FROM "Users";
```

# 実行環境

OS: Windows 11 Pro

Docker: Docker Desktop 4.26.1

PostgreSQL: psql (PostgreSQL) 17.2 (Debian 17.2-1.pgdg120+1)

Yarn: 4.4.1

Prisma: 6.0.1

# 実際の挙動確認

## PostgreSQLのDockerコンテナ立ち上げ

今回はcompose.ymlファイルを作成して、PostgreSQLのコンテナを立ち上げました。

```yaml
services:
  db:
    image: postgres
    container_name: potgres-for-qiita
    ports:
      - 5433:5432
    volumes:
      - db-store:/var/lib/potgresql/data
    environment:
      POSTGRES_USER: 'qiitablog'
      POSTGRES_PASSWORD: 'qiitapass'

volumes:
  db-store:
```

私はすでにビルドしているので、シンプルなログですが、初回の場合はイメージのPullやネットワーク、ボリュームの構築で他のログも表示されるかと思います。

```bash
> docker compose up -d
[+] Running 1/1
 ✔ Container potgres-for-qiita  Started                                                                            0.1s
```

立ち上げたコンテナ上で状態を確認してみます。

qiitablogというデータベースも一緒に作成されていることが分かります。

```bash
# psql -U qiitablog
psql (17.2 (Debian 17.2-1.pgdg120+1))
Type "help" for help.

qiitablog=# \l
                                                      List of databases
   Name    |   Owner   | Encoding | Locale Provider |  Collate   |   Ctype    | Locale | ICU Rules |    Access privileges    
-----------+-----------+----------+-----------------+------------+------------+--------+-----------+-------------------------
 postgres  | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | 
 qiitablog | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | 
 template0 | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/qiitablog           +
           |           |          |                 |            |            |        |           | qiitablog=CTc/qiitablog
 template1 | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/qiitablog           +
           |           |          |                 |            |            |        |           | qiitablog=CTc/qiitablog
(4 rows)
```

## Prismaでのデータベース構築

Yarnを使用してPrismaを導入していきます。(最新バージョンを使っていないのがバレる。)

```bash
> yarn init
➤ YN0088: A new stable version of Yarn is available: 4.5.3!
➤ YN0088: Upgrade now by running yarn set version 4.5.3

➤ YN0000: · Yarn 4.4.1
➤ YN0000: ┌ Resolution step
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: └ Completed
➤ YN0000: · Done in 0s 58ms
```

Yarnで必要なものを追加しておきます。

```bash
> yarn add prisma typescript tsx @types/node --dev
➤ YN0000: · Yarn 4.4.1
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + @types/node@npm:22.10.1, prisma@npm:6.0.1, tsx@npm:4.19.2, and 125 more.
➤ YN0000: └ Completed in 1s 947ms
➤ YN0000: ┌ Fetch step
➤ YN0066: │ typescript@patch:typescript@npm%3A5.7.2#optional!builtin<compat/typescript>::version=5.7.2&hash=8c6c40: Cannot apply hunk #1
➤ YN0013: │ 36 packages were added to the project (+ 61.83 MiB).
➤ YN0000: └ Completed in 0s 838ms
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0007: │ @prisma/engines@npm:6.0.1 must be built because it never has been before or the last one failed
➤ YN0007: │ esbuild@npm:0.23.1 must be built because it never has been before or the last one failed
➤ YN0007: │ prisma@npm:6.0.1 must be built because it never has been before or the last one failed
➤ YN0000: └ Completed in 2s 358ms
➤ YN0000: · Done with warnings in 5s 166ms
```

後で必要なので@prisma/clientも追加しておきます。

```bash
> yarn add @prisma/client
```

Prismaを初期化します。親切なことに、次にやるべきことを記載してくれていますね。

```bash
> yarn prisma init

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.

warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.

Next steps:
1. Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read https://pris.ly/d/getting-started
2. Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.
3. Run prisma db pull to turn your database schema into a Prisma schema.
4. Run prisma generate to generate the Prisma Client. You can then start querying your database.
5. Tip: Explore how you can extend the ORM with scalable connection pooling, global caching, and real-time database events. Read: https://pris.ly/cli/beyond-orm

More information in our documentation:
https://pris.ly/d/getting-started
```

まずは、.envファイルに今回使うデータベースへの接続情報を記載します。

データベース名は今回あえて現状存在しない、”db_from_prisma”にしてみました。

```bash
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://qiitablog:qiitapass@localhost:5433/db_from_prisma?schema=public"
```

次は、Prismaの初期化時に作成された/prisma/shema.prismaファイルにモデルを追加しましょう。

今回は公式のサンプルのモデルとほぼ同じもの（User）を作成します。

```typescript
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}
```

次に、Prisma Clientを作成します。

```bash
> yarn prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma

✔ Generated Prisma Client (v6.0.1) to .\node_modules\@prisma\client in 30ms

Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)

Tip: Need your database queries to be 1000x faster? Accelerate offers you that and more: https://pris.ly/tip-2-accelerate
```

:::note warn
yarn prisma generateがうまくいかない場合
もし、prisma generateコマンドを実行した際に、prismaや@prisma/clientをインストールしたのに、入っていないようなエラーが発生した場合は、YarnのPnP機能が原因かもしれません。

その際は、最新版のYarnの特徴を一つ消してしまうことになりますが、.yarnrc.ymlというファイルをプロジェクトルートの直下に作成して、下記のように記述します。

```yaml
nodeLinker: node-modules
```

その後、yarnコマンドでインストールするとnode_modulesフォルダの下にインストールしたモジュールが追加され、yarn prisma generateコマンドが実行できるようになります。

```bash
>yarn
```
:::

そうしたら、shema.prismaに合わせてデータベースを構築するためのマイグレーションを実行します。

ここまでやったら、

```bash
> yarn prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": PostgreSQL database "db_from_prisma", schema "public" at "localhost:5433"

PostgreSQL database db_from_prisma created at localhost:5433

Applying migration `20241208100832_init`

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

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

Your database is now in sync with your schema.

✔ Generated Prisma Client (v6.0.1) to .\node_modules\@prisma\client in 30ms
```

## PostgreSQLコマンドでの確認

PostgreSQLのコンテナ立ち上げ後と同じように、コマンドで確かめてみます。

```bash
# psql -U qiitablog
psql (17.2 (Debian 17.2-1.pgdg120+1))
Type "help" for help.

qiitablog=# \l
                                                        List of databases
      Name      |   Owner   | Encoding | Locale Provider |  Collate   |   Ctype    | Locale | ICU Rules |    Access privileges    
----------------+-----------+----------+-----------------+------------+------------+--------+-----------+-------------------------
 db_from_prisma | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | 
 postgres       | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | 
 qiitablog      | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | 
 template0      | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/qiitablog           +
                |           |          |                 |            |            |        |           | qiitablog=CTc/qiitablog
 template1      | qiitablog | UTF8     | libc            | en_US.utf8 | en_US.utf8 |        |           | =c/qiitablog           +
                |           |          |                 |            |            |        |           | qiitablog=CTc/qiitablog
(5 rows)
```

db_from_prismaというデータベースが増えてます！成功です！

ちゃんと接続できるかも確かめてみます。

```bash
qiitablog=# \c db_from_prisma
You are now connected to database "db_from_prisma" as user "qiitablog"
db_from_prisma=#
```

db_from_prismaに接続できました！

では、Userテーブルが作成されているかも見てみましょう。

```bash
db_from_prisma=# \dt
                List of relations
 Schema |        Name        | Type  |   Owner   
--------+--------------------+-------+-----------
 public | User               | table | qiitablog
 public | _prisma_migrations | table | qiitablog
(2 rows)
```

作成されています！すべてが順調です！

まだ、なにもINSERTしていないですが、ちょっとSELECT文なんか使ってみましょうか。

```bash
db_from_prisma=# select * from User;
   user    
-----------
 qiitablog
(1 row)
```

少し様子が変です。schema.prismaで設定したidとかemail、nameが表示されません。

## 解決策

冒頭の結論でお話したように、PostgreSQLではテーブル名は頭文字が小文字であることを前提にしているで、このままだと勝手に小文字として解釈されて正しく表示することができないようです。

頭文字が大文字のテーブルに対してクエリしたい場合は、テーブル名をダブルクオーテーション（”）で囲ってあげます。

そうすることで、欲しいかった情報を手に入れることができました！

```bash
db_from_prisma=# select * from "User";
 id | email | name 
----+-------+------
(0 rows)
```

でも、毎回”User”にしなくてはいけないと、バグの原因になりそうです。

その場合は、@@map()をschema.prismaで使用すると良いみたいです。

```tsx
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?

  @@map("users")
}
```

schema.prismaを変更したら、再度Clientへの反映とデータベースのマイグレーションを実行してみます。

```bash
> yarn prisma generate
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma

✔ Generated Prisma Client (v6.0.1) to .\node_modules\@prisma\client in 30ms

Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)

Tip: Want real-time updates to your database without manual polling? Discover how with Pulse: https://pris.ly/tip-0-pulse

> yarn prisma migrate dev --name init
Environment variables loaded from .env
Prisma schema loaded from prisma\schema.prisma
Datasource "db": PostgreSQL database "db_from_prisma", schema "public" at "localhost:5433"

Applying migration `20241208102252_init`

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

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

Your database is now in sync with your schema.

✔ Generated Prisma Client (v6.0.1) to .\node_modules\@prisma\client in 29ms
```

どうなっているか、Prismaのコンテナ上で確かめてみます。

同じdb_from_prismaデータベース内に、usersというテーブルが出現しました。

```bash
db_from_prisma=# \dt
                List of relations
 Schema |        Name        | Type  |   Owner   
--------+--------------------+-------+-----------
 public | _prisma_migrations | table | qiitablog
 public | users              | table | qiitablog
(2 rows)
```

当然、この場合は、普通にクエリして結果を得ることができます！

```bash
db_from_prisma=# select * from users;
 id | email | name 
----+-------+------
(0 rows)
```

# おわりに

今回は、PrismaでPostgreSQLを扱うときに私が詰まった箇所の再現と解決策を紹介してみました。

データを操作する以前の状態で詰まるとは…。

いやはや、この先の道のりはまだまだ長そうです。