0
0

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でハマる「The column does not exist」エラーの原因と対策

Posted at

はじめに

Next.js + Prisma + PostgreSQLで開発していて、こんなエラーに遭遇したことはありませんか?

The column `contents.wordCount` does not exist in the current database.

「ローカルでは動くのに、本番環境でエラーが出る!」

今回は、この問題の原因と解決方法を、データベースを触ったことがない人でも理解できるよう、1から詳しく解説します。

目次

  1. そもそもデータベースのスキーマって何?
  2. Prismaって何をしてくれるの?
  3. なぜスキーマの不一致が起きるのか
  4. 実際に起きた問題の詳細
  5. 5つの解決方法
  6. 今後同じ問題を防ぐためのベストプラクティス

1. そもそもデータベースのスキーマって何?

Excelで考えてみよう

データベースのスキーマを理解する最も簡単な方法は、Excelに例えることです。

Excelシート = データベースのテーブル
列のヘッダー = スキーマ

例:顧客管理シート
┌────┬──────┬──────┬─────────┐
│ A  │  B   │  C   │    D    │
├────┼──────┼──────┼─────────┤
│ID  │ 名前 │ 年齢 │ メール  │ <- これがスキーマ!
├────┼──────┼──────┼─────────┤
│001 │ 田中 │  25  │ xxx@... │
│002 │ 鈴木 │  30  │ yyy@... │
└────┴──────┴──────┴─────────┘

データベースでは

-- これがスキーマの定義
CREATE TABLE customers (
  id VARCHAR(255),
  name VARCHAR(255),
  age INTEGER,
  email VARCHAR(255)
);

スキーマとは「どんな列があって、それぞれどんな種類のデータを入れるか」を決めた設計図です。

2. Prismaって何をしてくれるの?

Prismaなしの世界

データベースと直接やり取りするには、SQL(エスキューエル)という言語を使います:

// 生のSQLを書く必要がある
const result = await db.query(
  'SELECT * FROM contents WHERE level = ? AND title LIKE ?',
  ['beginner', '%JavaScript%']
);

// エラーが起きやすい
// - SQLの文法ミス
// - テーブル名や列名のタイプミス
// - データ型の不一致

Prismaありの世界

Prismaは、JavaScriptの書き方でデータベースを操作できるようにしてくれます:

// JavaScriptらしい書き方でOK!
const result = await prisma.content.findMany({
  where: {
    level: 'beginner',
    title: { contains: 'JavaScript' }
  }
});

// メリット
// - 自動補完が効く
// - 型チェックでエラーを防げる
// - SQLを覚えなくてもOK

Prismaの仕組み

schema.prisma(設計図)
     ↓
Prismaが変換
     ↓
実際のデータベース

重要なのは、設計図と実際のデータベースを同期させる必要があるということです。

3. なぜスキーマの不一致が起きるのか

よくあるパターン1: 更新忘れ

開発の流れ:
1. ローカルで新しい機能を開発
   └─ schema.prismaに列を追加
   └─ prisma db push で反映 ✓

2. コードをGitHubにプッシュ
   └─ schema.prismaもプッシュ ✓

3. 本番環境にデプロイ
   └─ コードは更新される ✓
   └─ でも、データベースは? ✗ ← ここが問題!

よくあるパターン2: 環境による差異

開発環境:
┌─────────────────────────────────┐
│ 開発者A: wordCount列あり ✓      │
│ 開発者B: wordCount列あり ✓      │
│ ステージング: wordCount列なし ✗ │
│ 本番: wordCount列なし ✗         │
└─────────────────────────────────┘

今回起きた問題

// schema.prisma(設計図)
model Content {
  id             String  @id
  title          String
  wordCount      Int?    // <- これが存在
  characterCount Int?    // <- これも存在
}

// コード(API)
const contents = await prisma.content.findMany({
  // wordCountとcharacterCountも取得しようとする
});

しかし、本番データベースには:

-- 実際のテーブル構造
CREATE TABLE contents (
  id TEXT,
  title TEXT
  -- wordCountとcharacterCountが存在しない!
);

4. エラーメッセージの読み解き方

Error: 
Invalid `prisma.content.findMany()` invocation:

The column `contents.wordCount` does not exist in the current database.

このエラーを分解すると:

  1. Invalid invocation = 「呼び出し方が無効です」
  2. prisma.content.findMany() = 「contentテーブルから複数件取得しようとした時」
  3. The column contents.wordCount does not exist = 「contentsテーブルのwordCount列が存在しません」
  4. in the current database = 「現在接続中のデータベースに」

つまり:「設計図には書いてあるけど、実際のデータベースにその列がないよ!」という意味です。

5. 5つの解決方法

解決法1: データベースを更新する(推奨)

# 本番環境で実行
prisma db push

# または、より安全なマイグレーション
prisma migrate deploy

解決法2: ビルドスクリプトに追加

// package.json
{
  "scripts": {
    "build": "prisma generate && prisma db push && next build"
  }
}

解決法3: 存在しない列を使わないAPI作成

// 問題のある列を除外
const contents = await prisma.content.findMany({
  select: {
    id: true,
    title: true,
    // wordCount: true, // <- 使わない
    // characterCount: true, // <- 使わない
  }
});

解決法4: エラーハンドリングで対処

try {
  const contents = await prisma.content.findMany();
  return contents;
} catch (error) {
  if (error.code === 'P2022') {
    // 列が存在しない場合の代替処理
    const contents = await prisma.content.findMany({
      select: { id: true, title: true }
    });
    return contents.map(c => ({
      ...c,
      wordCount: null,
      characterCount: c.title.length
    }));
  }
  throw error;
}

解決法5: 環境変数で切り替え

const isProduction = process.env.NODE_ENV === 'production';

const contents = await prisma.content.findMany({
  select: {
    id: true,
    title: true,
    ...(isProduction ? {} : {
      wordCount: true,
      characterCount: true
    })
  }
});

6. 今後同じ問題を防ぐベストプラクティス

1. マイグレーションファイルを使う

# 開発時
prisma migrate dev --name add_word_count

# 本番環境
prisma migrate deploy

2. CI/CDパイプラインに組み込む

# .github/workflows/deploy.yml
steps:
  - name: Apply database migrations
    run: prisma migrate deploy
    
  - name: Deploy application
    run: vercel deploy --prod

3. スキーマの同期チェックスクリプト

// scripts/check-db-sync.js
const { PrismaClient } = require('@prisma/client');

async function checkSync() {
  const prisma = new PrismaClient();
  
  try {
    // テストクエリを実行
    await prisma.content.findFirst();
    console.log('✅ Database schema is in sync');
  } catch (error) {
    if (error.code === 'P2022') {
      console.error('❌ Database schema mismatch detected!');
      console.error('Missing columns:', error.meta?.column);
      process.exit(1);
    }
  }
}

checkSync();

4. 環境ごとのドキュメント化

## Database Schema Status

| Environment | wordCount | characterCount | Last Updated |
|-------------|-----------|----------------|--------------|
| Local       | ✅        | ✅             | 2024-01-15   |
| Staging     | ✅        | ✅             | 2024-01-15   |
| Production  | ❌        | ❌             | 2024-01-10   |

まとめ

「The column does not exist」エラーは、設計図(schema.prisma)と実際のデータベースがズレていることが原因です。

覚えておくべき3つのポイント

  1. Prismaは設計図 - 実際のデータベースとは別物
  2. 同期が必要 - prisma db pushprisma migrateで反映
  3. 環境ごとに管理 - 開発・ステージング・本番で差異が生まれやすい

チェックリスト

  • schema.prismaを変更したら、必ずデータベースも更新する
  • デプロイ前に、本番環境のスキーマを確認する
  • エラーハンドリングで、スキーマ不一致に対応する
  • CI/CDにスキーマ同期を組み込む

これらを実践すれば、同じエラーで悩むことはなくなるはずです!

参考リンク

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?